jvserve 2.1.2__tar.gz → 2.1.4__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.
Potentially problematic release.
This version of jvserve might be problematic. Click here for more details.
- {jvserve-2.1.2 → jvserve-2.1.4}/PKG-INFO +2 -1
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/cli.py +118 -11
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve.egg-info/PKG-INFO +2 -1
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve.egg-info/requires.txt +1 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/setup.py +1 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/LICENSE +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/README.md +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/__init__.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/lib/__init__.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/lib/agent_interface.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/lib/file_interface.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/lib/jac_interface.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve/lib/jvlogger.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve.egg-info/SOURCES.txt +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve.egg-info/dependency_links.txt +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve.egg-info/entry_points.txt +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/jvserve.egg-info/top_level.txt +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/setup.cfg +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/tests/test_file_interface.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/tests/test_jvlogger.py +0 -0
- {jvserve-2.1.2 → jvserve-2.1.4}/tests/test_jvserve.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jvserve
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: FastAPI webserver for loading and interaction with JIVAS agents.
|
|
5
5
|
Home-page: https://github.com/TrueSelph/jvserve
|
|
6
6
|
Author: TrueSelph Inc.
|
|
@@ -10,6 +10,7 @@ Requires-Python: >=3.12.0
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: jac-cloud
|
|
13
|
+
Requires-Dist: psutil>=7.0.0
|
|
13
14
|
Requires-Dist: jaclang==0.8.4
|
|
14
15
|
Requires-Dist: pyaml>=25.1.0
|
|
15
16
|
Requires-Dist: requests>=2.32.3
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
+
import sys
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
6
9
|
from concurrent.futures import ThreadPoolExecutor
|
|
7
10
|
from contextlib import asynccontextmanager
|
|
8
11
|
from pickle import load
|
|
9
12
|
from typing import AsyncIterator, Optional
|
|
10
13
|
|
|
11
14
|
import aiohttp
|
|
15
|
+
import psutil
|
|
12
16
|
import pymongo
|
|
13
17
|
from bson import ObjectId
|
|
14
18
|
from dotenv import load_dotenv
|
|
@@ -21,7 +25,7 @@ from jac_cloud.plugin.jaseci import NodeAnchor
|
|
|
21
25
|
from jaclang import JacMachine as Jac
|
|
22
26
|
from jaclang.cli.cmdreg import cmd_registry
|
|
23
27
|
from jaclang.runtimelib.machine import hookimpl
|
|
24
|
-
from watchfiles import Change,
|
|
28
|
+
from watchfiles import Change, watch
|
|
25
29
|
|
|
26
30
|
from jvserve.lib.agent_interface import AgentInterface
|
|
27
31
|
from jvserve.lib.file_interface import (
|
|
@@ -43,6 +47,10 @@ logger = logging.getLogger(__name__)
|
|
|
43
47
|
url_proxy_collection = None
|
|
44
48
|
collection_init_lock = asyncio.Lock()
|
|
45
49
|
|
|
50
|
+
# Global state for watcher control
|
|
51
|
+
WATCHER_STATE_FILE = ".jvserve_watcher_state"
|
|
52
|
+
watcher_enabled = True
|
|
53
|
+
|
|
46
54
|
|
|
47
55
|
async def get_url_proxy_collection() -> pymongo.collection.Collection:
|
|
48
56
|
"""Thread-safe initialization of MongoDB collection"""
|
|
@@ -96,6 +104,37 @@ async def serve_proxied_file(
|
|
|
96
104
|
raise HTTPException(status_code=502, detail=f"File fetch error: {str(e)}")
|
|
97
105
|
|
|
98
106
|
|
|
107
|
+
def start_file_watcher(
|
|
108
|
+
watchdir: str, filename: str, host: str, port: int
|
|
109
|
+
) -> threading.Thread:
|
|
110
|
+
"""Start the file watcher in a separate thread"""
|
|
111
|
+
|
|
112
|
+
def watcher_loop() -> None:
|
|
113
|
+
"""File watcher loop that runs in a separate thread"""
|
|
114
|
+
global watcher_enabled
|
|
115
|
+
|
|
116
|
+
logger.info(f"Starting file watcher for directory: {watchdir}")
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
for changes in watch(watchdir):
|
|
120
|
+
if watcher_enabled:
|
|
121
|
+
log_reload(changes)
|
|
122
|
+
# Kill the current server process and restart
|
|
123
|
+
reload_server()
|
|
124
|
+
else:
|
|
125
|
+
logger.info("Watcher disabled, ignoring changes")
|
|
126
|
+
time.sleep(1) # Prevent busy loop when disabled
|
|
127
|
+
except KeyboardInterrupt:
|
|
128
|
+
logger.info("File watcher stopped")
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.error(f"File watcher error: {e}")
|
|
131
|
+
|
|
132
|
+
# Start watcher in daemon thread so it doesn't prevent program exit
|
|
133
|
+
watcher_thread = threading.Thread(target=watcher_loop, daemon=True)
|
|
134
|
+
watcher_thread.start()
|
|
135
|
+
return watcher_thread
|
|
136
|
+
|
|
137
|
+
|
|
99
138
|
def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
100
139
|
"""Starts JIVAS server with integrated file services"""
|
|
101
140
|
|
|
@@ -219,12 +258,32 @@ def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
|
219
258
|
raise HTTPException(status_code=500, detail="Internal server error")
|
|
220
259
|
|
|
221
260
|
ctx.close()
|
|
261
|
+
|
|
262
|
+
# Start file watcher BEFORE starting the server (in development mode)
|
|
263
|
+
is_development = os.environ.get("JIVAS_ENVIRONMENT") == "development"
|
|
264
|
+
if is_development:
|
|
265
|
+
watchdir = os.path.join(
|
|
266
|
+
os.path.abspath(os.path.dirname(filename)), "actions", ""
|
|
267
|
+
)
|
|
268
|
+
logger.info("Development mode: Starting file watcher")
|
|
269
|
+
enable_watcher()
|
|
270
|
+
start_file_watcher(watchdir, filename, host, port)
|
|
271
|
+
|
|
222
272
|
# Run the app
|
|
223
273
|
JaseciFastAPI.start(host=host, port=port)
|
|
224
274
|
|
|
225
275
|
|
|
226
276
|
def log_reload(changes: set[tuple[Change, str]]) -> None:
|
|
227
|
-
"""Log changes."""
|
|
277
|
+
"""Log changes and check watcher state."""
|
|
278
|
+
global watcher_enabled
|
|
279
|
+
|
|
280
|
+
logger.warning(f"Watcher is: {watcher_enabled}")
|
|
281
|
+
|
|
282
|
+
# Check if watcher is disabled
|
|
283
|
+
if not watcher_enabled:
|
|
284
|
+
logger.warning("Watcher is disabled. Ignoring changes.")
|
|
285
|
+
return
|
|
286
|
+
|
|
228
287
|
num_of_changes = len(changes)
|
|
229
288
|
logger.warning(
|
|
230
289
|
f'Detected {num_of_changes} change{"s" if num_of_changes > 1 else ""}'
|
|
@@ -245,12 +304,60 @@ class JacCmd:
|
|
|
245
304
|
@cmd_registry.register
|
|
246
305
|
def jvserve(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
247
306
|
"""Launch unified JIVAS server with file services"""
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
307
|
+
run_jivas(filename, host, port)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def disable_watcher() -> dict:
|
|
311
|
+
"""Disable the watcher from auto-reloading"""
|
|
312
|
+
if os.environ.get("JIVAS_ENVIRONMENT") == "development":
|
|
313
|
+
global watcher_enabled
|
|
314
|
+
watcher_enabled = False
|
|
315
|
+
with open(WATCHER_STATE_FILE, "w") as f:
|
|
316
|
+
f.write("disabled")
|
|
317
|
+
return {"message": "Watcher disabled"}
|
|
318
|
+
else:
|
|
319
|
+
return {"message": "Watcher already disabled"}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def enable_watcher() -> dict:
|
|
323
|
+
"""Enable the watcher for auto-reloading"""
|
|
324
|
+
if os.environ.get("JIVAS_ENVIRONMENT") == "development":
|
|
325
|
+
global watcher_enabled
|
|
326
|
+
watcher_enabled = True
|
|
327
|
+
with open(WATCHER_STATE_FILE, "w") as f:
|
|
328
|
+
f.write("enabled")
|
|
329
|
+
return {"message": "Watcher enabled"}
|
|
330
|
+
else:
|
|
331
|
+
return {"message": "Watcher already enabled"}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def reload_server() -> None:
|
|
335
|
+
"""Reload the server using the exact command that started it."""
|
|
336
|
+
try:
|
|
337
|
+
# Get the command used to start the sever
|
|
338
|
+
current_process = psutil.Process(os.getpid())
|
|
339
|
+
cmdline = current_process.cmdline()
|
|
340
|
+
|
|
341
|
+
logger.info(f"Restarting with command: {' '.join(cmdline)}")
|
|
342
|
+
|
|
343
|
+
# Replace current process with the same command
|
|
344
|
+
os.execvp(cmdline[0], cmdline)
|
|
345
|
+
|
|
346
|
+
except Exception as e:
|
|
347
|
+
logger.error(f"Failed to get process command line: {e}")
|
|
348
|
+
# Fallback to sys.argv
|
|
349
|
+
reload_server_from_argv()
|
|
350
|
+
finally:
|
|
351
|
+
is_development = os.environ.get("JIVAS_ENVIRONMENT") == "development"
|
|
352
|
+
|
|
353
|
+
if is_development:
|
|
354
|
+
enable_watcher()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def reload_server_from_argv() -> None:
|
|
358
|
+
"""Reload using sys.argv (the original command line arguments)."""
|
|
359
|
+
logger.info("Reloading server using sys.argv...")
|
|
360
|
+
logger.info(f"Original command: {' '.join(sys.argv)}")
|
|
361
|
+
|
|
362
|
+
# sys.argv[0] is the script name, rest are arguments
|
|
363
|
+
os.execvp(sys.executable, [sys.executable] + sys.argv)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jvserve
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: FastAPI webserver for loading and interaction with JIVAS agents.
|
|
5
5
|
Home-page: https://github.com/TrueSelph/jvserve
|
|
6
6
|
Author: TrueSelph Inc.
|
|
@@ -10,6 +10,7 @@ Requires-Python: >=3.12.0
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: jac-cloud
|
|
13
|
+
Requires-Dist: psutil>=7.0.0
|
|
13
14
|
Requires-Dist: jaclang==0.8.4
|
|
14
15
|
Requires-Dist: pyaml>=25.1.0
|
|
15
16
|
Requires-Dist: requests>=2.32.3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|