jvserve 2.1.6__tar.gz → 2.1.8__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.6 → jvserve-2.1.8}/PKG-INFO +1 -1
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/cli.py +56 -62
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve.egg-info/PKG-INFO +1 -1
- {jvserve-2.1.6 → jvserve-2.1.8}/LICENSE +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/README.md +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/__init__.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/lib/__init__.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/lib/agent_interface.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/lib/file_interface.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/lib/jac_interface.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve/lib/jvlogger.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve.egg-info/SOURCES.txt +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve.egg-info/dependency_links.txt +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve.egg-info/entry_points.txt +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve.egg-info/requires.txt +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/jvserve.egg-info/top_level.txt +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/setup.cfg +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/setup.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/tests/test_file_interface.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/tests/test_jvlogger.py +0 -0
- {jvserve-2.1.6 → jvserve-2.1.8}/tests/test_jvserve.py +0 -0
|
@@ -21,6 +21,8 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
21
21
|
from fastapi.responses import FileResponse, StreamingResponse
|
|
22
22
|
from jac_cloud.core.context import JaseciContext
|
|
23
23
|
from jac_cloud.jaseci.main import FastAPI as JaseciFastAPI # type: ignore
|
|
24
|
+
from jac_cloud.jaseci.utils import logger
|
|
25
|
+
from jac_cloud.jaseci.utils.logger import Level
|
|
24
26
|
from jac_cloud.plugin.jaseci import NodeAnchor
|
|
25
27
|
from jaclang import JacMachine as Jac
|
|
26
28
|
from jaclang.cli.cmdreg import cmd_registry
|
|
@@ -35,20 +37,18 @@ from jvserve.lib.file_interface import (
|
|
|
35
37
|
)
|
|
36
38
|
from jvserve.lib.jvlogger import JVLogger
|
|
37
39
|
|
|
38
|
-
# quiet the jac_cloud logger down to errors only
|
|
39
|
-
# jac cloud dumps payload details to console which makes it hard to debug in JIVAS
|
|
40
|
-
os.environ["LOGGER_LEVEL"] = "ERROR"
|
|
41
40
|
load_dotenv(".env")
|
|
42
|
-
#
|
|
41
|
+
# quiet the jac_cloud logger down to errors only
|
|
42
|
+
logger.setLevel(Level.ERROR.value)
|
|
43
|
+
# Set up logging for JIVAS
|
|
43
44
|
JVLogger.setup_logging(level="INFO")
|
|
44
|
-
|
|
45
|
+
jvlogger = logging.getLogger(__name__)
|
|
45
46
|
|
|
46
47
|
# Global for MongoDB collection with thread-safe initialization
|
|
47
48
|
url_proxy_collection = None
|
|
48
49
|
collection_init_lock = asyncio.Lock()
|
|
49
50
|
|
|
50
51
|
# Global state for watcher control
|
|
51
|
-
WATCHER_STATE_FILE = ".jvserve_watcher_state"
|
|
52
52
|
watcher_enabled = True
|
|
53
53
|
|
|
54
54
|
|
|
@@ -113,21 +113,21 @@ def start_file_watcher(
|
|
|
113
113
|
"""File watcher loop that runs in a separate thread"""
|
|
114
114
|
global watcher_enabled
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
jvlogger.info(f"Starting file watcher for directory: {watchdir}")
|
|
117
117
|
|
|
118
118
|
try:
|
|
119
119
|
for changes in watch(watchdir):
|
|
120
120
|
if watcher_enabled:
|
|
121
121
|
log_reload(changes)
|
|
122
122
|
# Kill the current server process and restart
|
|
123
|
-
|
|
123
|
+
reload_jivas()
|
|
124
124
|
else:
|
|
125
|
-
|
|
125
|
+
jvlogger.info("Watcher disabled, ignoring changes")
|
|
126
126
|
time.sleep(1) # Prevent busy loop when disabled
|
|
127
127
|
except KeyboardInterrupt:
|
|
128
|
-
|
|
128
|
+
jvlogger.info("File watcher stopped")
|
|
129
129
|
except Exception as e:
|
|
130
|
-
|
|
130
|
+
jvlogger.error(f"File watcher error: {e}")
|
|
131
131
|
|
|
132
132
|
# Start watcher in daemon thread so it doesn't prevent program exit
|
|
133
133
|
watcher_thread = threading.Thread(target=watcher_loop, daemon=True)
|
|
@@ -169,28 +169,28 @@ def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
|
169
169
|
async with aiohttp.ClientSession() as session:
|
|
170
170
|
async with session.get(health_url, timeout=1) as response:
|
|
171
171
|
if response.status == 200:
|
|
172
|
-
|
|
172
|
+
jvlogger.info("Server is ready, initializing agents...")
|
|
173
173
|
await agent_interface.init_agents()
|
|
174
174
|
return
|
|
175
175
|
except (aiohttp.ClientConnectorError, asyncio.TimeoutError) as e:
|
|
176
|
-
|
|
176
|
+
jvlogger.warning(
|
|
177
177
|
f"Server not ready yet (attempt {attempt + 1} / {max_retries}): {e}"
|
|
178
178
|
)
|
|
179
179
|
await asyncio.sleep(retry_delay)
|
|
180
180
|
retry_delay *= 1.5 # Exponential backoff
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
jvlogger.error(
|
|
183
183
|
"Server did not become ready in time. Agent initialization skipped."
|
|
184
184
|
)
|
|
185
185
|
|
|
186
186
|
# set up lifespan events
|
|
187
187
|
async def on_startup() -> None:
|
|
188
|
-
|
|
188
|
+
jvlogger.info("JIVAS is starting up...")
|
|
189
189
|
# Start initialization in background without blocking
|
|
190
190
|
asyncio.create_task(post_startup())
|
|
191
191
|
|
|
192
192
|
async def on_shutdown() -> None:
|
|
193
|
-
|
|
193
|
+
jvlogger.info("JIVAS is shutting down...")
|
|
194
194
|
|
|
195
195
|
app = JaseciFastAPI.get()
|
|
196
196
|
app_lifespan = app.router.lifespan_context
|
|
@@ -254,7 +254,7 @@ def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
|
254
254
|
|
|
255
255
|
raise HTTPException(status_code=404, detail="File not found")
|
|
256
256
|
except Exception as e:
|
|
257
|
-
|
|
257
|
+
jvlogger.error(f"Proxy error: {str(e)}")
|
|
258
258
|
raise HTTPException(status_code=500, detail="Internal server error")
|
|
259
259
|
|
|
260
260
|
ctx.close()
|
|
@@ -265,8 +265,7 @@ def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
|
265
265
|
watchdir = os.path.join(
|
|
266
266
|
os.path.abspath(os.path.dirname(filename)), "actions", ""
|
|
267
267
|
)
|
|
268
|
-
|
|
269
|
-
enable_watcher()
|
|
268
|
+
jvlogger.info("Development mode: Starting file watcher")
|
|
270
269
|
start_file_watcher(watchdir, filename, host, port)
|
|
271
270
|
|
|
272
271
|
# Run the app
|
|
@@ -277,34 +276,19 @@ def log_reload(changes: set[tuple[Change, str]]) -> None:
|
|
|
277
276
|
"""Log changes and check watcher state."""
|
|
278
277
|
global watcher_enabled
|
|
279
278
|
|
|
280
|
-
|
|
279
|
+
jvlogger.warning(f"Watcher is: {watcher_enabled}")
|
|
281
280
|
|
|
282
281
|
# Check if watcher is disabled
|
|
283
282
|
if not watcher_enabled:
|
|
284
|
-
logger.warning("Watcher is disabled. Ignoring changes.")
|
|
285
283
|
return
|
|
286
284
|
|
|
287
285
|
num_of_changes = len(changes)
|
|
288
|
-
|
|
286
|
+
jvlogger.warning(
|
|
289
287
|
f'Detected {num_of_changes} change{"s" if num_of_changes > 1 else ""}'
|
|
290
288
|
)
|
|
291
289
|
for change in changes:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
class JacCmd:
|
|
297
|
-
"""Jac CLI."""
|
|
298
|
-
|
|
299
|
-
@staticmethod
|
|
300
|
-
@hookimpl
|
|
301
|
-
def create_cmd() -> None:
|
|
302
|
-
"""Create Jac CLI cmds."""
|
|
303
|
-
|
|
304
|
-
@cmd_registry.register
|
|
305
|
-
def jvserve(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
306
|
-
"""Launch unified JIVAS server with file services"""
|
|
307
|
-
run_jivas(filename, host, port)
|
|
290
|
+
jvlogger.warning(f"{change[1]} ({change[0].name})")
|
|
291
|
+
jvlogger.warning("Reloading ...")
|
|
308
292
|
|
|
309
293
|
|
|
310
294
|
def disable_watcher() -> dict:
|
|
@@ -312,8 +296,6 @@ def disable_watcher() -> dict:
|
|
|
312
296
|
if os.environ.get("JIVAS_ENVIRONMENT") == "development":
|
|
313
297
|
global watcher_enabled
|
|
314
298
|
watcher_enabled = False
|
|
315
|
-
with open(WATCHER_STATE_FILE, "w") as f:
|
|
316
|
-
f.write("disabled")
|
|
317
299
|
return {"message": "Watcher disabled"}
|
|
318
300
|
else:
|
|
319
301
|
return {"message": "Watcher already disabled"}
|
|
@@ -324,40 +306,52 @@ def enable_watcher() -> dict:
|
|
|
324
306
|
if os.environ.get("JIVAS_ENVIRONMENT") == "development":
|
|
325
307
|
global watcher_enabled
|
|
326
308
|
watcher_enabled = True
|
|
327
|
-
with open(WATCHER_STATE_FILE, "w") as f:
|
|
328
|
-
f.write("enabled")
|
|
329
309
|
return {"message": "Watcher enabled"}
|
|
330
310
|
else:
|
|
331
311
|
return {"message": "Watcher already enabled"}
|
|
332
312
|
|
|
333
313
|
|
|
334
|
-
def
|
|
335
|
-
"""Reload the server
|
|
314
|
+
def reload_jivas() -> None:
|
|
315
|
+
"""Reload the server, handling virtual environments robustly."""
|
|
336
316
|
try:
|
|
337
|
-
#
|
|
317
|
+
# Use sys.executable to ensure correct Python interpreter (handles venv)
|
|
338
318
|
current_process = psutil.Process(os.getpid())
|
|
339
319
|
cmdline = current_process.cmdline()
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
320
|
+
if cmdline:
|
|
321
|
+
# Replace executable with sys.executable if different (for venv safety)
|
|
322
|
+
exec_path = sys.executable
|
|
323
|
+
if os.path.exists(exec_path):
|
|
324
|
+
cmdline[0] = exec_path
|
|
325
|
+
jvlogger.info(f"Restarting with command: {' '.join(cmdline)}")
|
|
326
|
+
os.execvp(exec_path, cmdline)
|
|
327
|
+
else:
|
|
328
|
+
raise RuntimeError("sys.executable does not exist")
|
|
329
|
+
else:
|
|
330
|
+
raise RuntimeError("Invalid cmdline from psutil")
|
|
346
331
|
except Exception as e:
|
|
347
|
-
|
|
348
|
-
# Fallback to sys.argv
|
|
349
|
-
|
|
350
|
-
finally:
|
|
351
|
-
is_development = os.environ.get("JIVAS_ENVIRONMENT") == "development"
|
|
352
|
-
|
|
353
|
-
if is_development:
|
|
354
|
-
enable_watcher()
|
|
332
|
+
jvlogger.error(f"Failed to reload using psutil cmdline: {e}")
|
|
333
|
+
# Fallback to sys.argv if psutil fails or cmdline is not usable
|
|
334
|
+
reload_jivas_from_argv()
|
|
355
335
|
|
|
356
336
|
|
|
357
|
-
def
|
|
337
|
+
def reload_jivas_from_argv() -> None:
|
|
358
338
|
"""Reload using sys.argv (the original command line arguments)."""
|
|
359
|
-
|
|
360
|
-
|
|
339
|
+
jvlogger.info("Reloading server using sys.argv...")
|
|
340
|
+
jvlogger.info(f"Original command: {' '.join(sys.argv)}")
|
|
361
341
|
|
|
362
342
|
# sys.argv[0] is the script name, rest are arguments
|
|
363
343
|
os.execvp(sys.executable, [sys.executable] + sys.argv)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class JacCmd:
|
|
347
|
+
"""Jac CLI."""
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
@hookimpl
|
|
351
|
+
def create_cmd() -> None:
|
|
352
|
+
"""Create Jac CLI cmds."""
|
|
353
|
+
|
|
354
|
+
@cmd_registry.register
|
|
355
|
+
def jvserve(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
356
|
+
"""Launch unified JIVAS server with file services"""
|
|
357
|
+
run_jivas(filename, host, port)
|
|
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
|
|
File without changes
|
|
File without changes
|