jvserve 2.1.17__py3-none-any.whl → 2.1.24__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.
- jvserve/cli.py +109 -2
- jvserve/lib/agent_interface.py +57 -0
- jvserve/lib/jac_interface.py +1 -1
- {jvserve-2.1.17.dist-info → jvserve-2.1.24.dist-info}/METADATA +9 -7
- jvserve-2.1.24.dist-info/RECORD +14 -0
- jvserve-2.1.17.dist-info/RECORD +0 -14
- {jvserve-2.1.17.dist-info → jvserve-2.1.24.dist-info}/WHEEL +0 -0
- {jvserve-2.1.17.dist-info → jvserve-2.1.24.dist-info}/entry_points.txt +0 -0
- {jvserve-2.1.17.dist-info → jvserve-2.1.24.dist-info}/licenses/LICENSE +0 -0
- {jvserve-2.1.17.dist-info → jvserve-2.1.24.dist-info}/top_level.txt +0 -0
jvserve/cli.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Module for registering CLI plugins for jaseci."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import json
|
|
4
5
|
import logging
|
|
5
6
|
import mimetypes
|
|
6
7
|
import os
|
|
@@ -9,6 +10,7 @@ import threading
|
|
|
9
10
|
import time
|
|
10
11
|
from concurrent.futures import ThreadPoolExecutor
|
|
11
12
|
from contextlib import asynccontextmanager
|
|
13
|
+
from datetime import datetime
|
|
12
14
|
from pickle import load
|
|
13
15
|
from typing import AsyncIterator, Optional
|
|
14
16
|
|
|
@@ -18,10 +20,11 @@ import pymongo
|
|
|
18
20
|
import requests
|
|
19
21
|
from bson import ObjectId
|
|
20
22
|
from dotenv import load_dotenv
|
|
21
|
-
from fastapi import FastAPI, HTTPException, Response
|
|
23
|
+
from fastapi import FastAPI, HTTPException, Request, Response
|
|
22
24
|
from fastapi.middleware.cors import CORSMiddleware
|
|
23
|
-
from fastapi.responses import FileResponse, StreamingResponse
|
|
25
|
+
from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
|
|
24
26
|
from jac_cloud.core.context import JaseciContext
|
|
27
|
+
from jac_cloud.jaseci.datasources.redis import Redis
|
|
25
28
|
from jac_cloud.jaseci.main import FastAPI as JaseciFastAPI # type: ignore
|
|
26
29
|
from jac_cloud.jaseci.utils import logger
|
|
27
30
|
from jac_cloud.jaseci.utils.logger import Level
|
|
@@ -41,6 +44,8 @@ from jvserve.lib.file_interface import (
|
|
|
41
44
|
)
|
|
42
45
|
from jvserve.lib.jvlogger import JVLogger
|
|
43
46
|
|
|
47
|
+
redis = Redis().get_rd()
|
|
48
|
+
|
|
44
49
|
load_dotenv(".env")
|
|
45
50
|
# quiet the jac_cloud logger down to errors only
|
|
46
51
|
logger.setLevel(Level.ERROR.value)
|
|
@@ -56,6 +61,10 @@ collection_init_lock = asyncio.Lock()
|
|
|
56
61
|
watcher_enabled = True
|
|
57
62
|
|
|
58
63
|
|
|
64
|
+
# taken from kubernetes HOSTNAME for replicated deployment
|
|
65
|
+
SERVER_ID = os.environ.get("HOSTNAME", "unknown_server")
|
|
66
|
+
|
|
67
|
+
|
|
59
68
|
async def get_url_proxy_collection() -> pymongo.collection.Collection:
|
|
60
69
|
"""Thread-safe initialization of MongoDB collection"""
|
|
61
70
|
global url_proxy_collection
|
|
@@ -244,6 +253,42 @@ def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
|
244
253
|
allow_headers=["*"],
|
|
245
254
|
)
|
|
246
255
|
|
|
256
|
+
@app.get("/action/webhook/{namespace}/{action}/{walker}/{agent_id}/{key}")
|
|
257
|
+
async def webhook_exec_get(
|
|
258
|
+
namespace: str,
|
|
259
|
+
action: str,
|
|
260
|
+
walker: str,
|
|
261
|
+
agent_id: str,
|
|
262
|
+
key: str,
|
|
263
|
+
request: Request,
|
|
264
|
+
) -> JSONResponse:
|
|
265
|
+
return await agent_interface.webhook_exec(
|
|
266
|
+
namespace=namespace,
|
|
267
|
+
action=action,
|
|
268
|
+
walker=walker,
|
|
269
|
+
agent_id=agent_id,
|
|
270
|
+
key=key,
|
|
271
|
+
request=request,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
@app.post("/action/webhook/{namespace}/{action}/{walker}/{agent_id}/{key}")
|
|
275
|
+
async def webhook_exec_post(
|
|
276
|
+
namespace: str,
|
|
277
|
+
action: str,
|
|
278
|
+
walker: str,
|
|
279
|
+
agent_id: str,
|
|
280
|
+
key: str,
|
|
281
|
+
request: Request,
|
|
282
|
+
) -> JSONResponse:
|
|
283
|
+
return await agent_interface.webhook_exec(
|
|
284
|
+
namespace=namespace,
|
|
285
|
+
action=action,
|
|
286
|
+
walker=walker,
|
|
287
|
+
agent_id=agent_id,
|
|
288
|
+
key=key,
|
|
289
|
+
request=request,
|
|
290
|
+
)
|
|
291
|
+
|
|
247
292
|
# Ensure the local file directory exists if that's the interface
|
|
248
293
|
if FILE_INTERFACE == "local":
|
|
249
294
|
directory = os.environ.get("JIVAS_FILES_ROOT_PATH", DEFAULT_FILES_ROOT)
|
|
@@ -299,6 +344,8 @@ def run_jivas(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
|
299
344
|
jvlogger.info("Development mode: Starting file watcher")
|
|
300
345
|
start_file_watcher(watchdir, filename, host, port)
|
|
301
346
|
|
|
347
|
+
threading.Thread(target=redis_listener, daemon=True).start()
|
|
348
|
+
|
|
302
349
|
# Run the app
|
|
303
350
|
JaseciFastAPI.start(host=host, port=port)
|
|
304
351
|
|
|
@@ -386,3 +433,63 @@ class JacCmd:
|
|
|
386
433
|
def jvserve(filename: str, host: str = "localhost", port: int = 8000) -> None:
|
|
387
434
|
"""Launch unified JIVAS server with file services"""
|
|
388
435
|
run_jivas(filename, host, port)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def handle_message(msg: str) -> None:
|
|
439
|
+
"""
|
|
440
|
+
Handles incoming messages from the Redis channel 'jivas_actions'.
|
|
441
|
+
Expects the message to be a JSON string with 'action' and 'initiator' fields.
|
|
442
|
+
Only acts on 'reload_jivas' messages not sent by this pod.
|
|
443
|
+
"""
|
|
444
|
+
try:
|
|
445
|
+
data = json.loads(msg)
|
|
446
|
+
except json.JSONDecodeError:
|
|
447
|
+
print(f"Received invalid message: {msg}")
|
|
448
|
+
return
|
|
449
|
+
|
|
450
|
+
action = data.get("action")
|
|
451
|
+
initiator = data.get("initiator")
|
|
452
|
+
|
|
453
|
+
# Ignore messages sent by this pod
|
|
454
|
+
if initiator == SERVER_ID:
|
|
455
|
+
jvlogger.info("Skipping message from self")
|
|
456
|
+
return
|
|
457
|
+
|
|
458
|
+
if action == "reload_jivas":
|
|
459
|
+
print(f"Received reload_jivas action from {initiator}, executing reload.")
|
|
460
|
+
reload_jivas()
|
|
461
|
+
else:
|
|
462
|
+
print(f"Ignored action: {action} from {initiator}")
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def redis_listener() -> None:
|
|
466
|
+
"""Listens to the Redis channel 'walker_install_action' and handles incoming messages."""
|
|
467
|
+
pubsub = redis.pubsub()
|
|
468
|
+
pubsub.subscribe("jivas_actions")
|
|
469
|
+
jvlogger.info("Subscribed to channel: jivas_actions")
|
|
470
|
+
|
|
471
|
+
for message in pubsub.listen():
|
|
472
|
+
if message["type"] == "message":
|
|
473
|
+
handle_message(message["data"])
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def send_action_notification(action: str, extra_data: dict | None = None) -> None:
|
|
477
|
+
"""
|
|
478
|
+
Sends a message to the Redis channel 'jivas_actions'.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
action (str): the action name, e.g., 'reload_jivas'
|
|
482
|
+
extra_data (dict): optional additional data to include in the message
|
|
483
|
+
"""
|
|
484
|
+
payload = {
|
|
485
|
+
"action": action,
|
|
486
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
487
|
+
"initiator": SERVER_ID,
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if extra_data:
|
|
491
|
+
payload.update(extra_data)
|
|
492
|
+
|
|
493
|
+
# Publish the message
|
|
494
|
+
redis.publish("jivas_actions", json.dumps(payload))
|
|
495
|
+
jvlogger.info(f"Sent message: {payload}")
|
jvserve/lib/agent_interface.py
CHANGED
|
@@ -6,6 +6,8 @@ import traceback
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
|
+
from fastapi import Request
|
|
10
|
+
from fastapi.responses import JSONResponse
|
|
9
11
|
|
|
10
12
|
from jvserve.lib.jac_interface import JacInterface
|
|
11
13
|
|
|
@@ -47,6 +49,61 @@ class AgentInterface:
|
|
|
47
49
|
self._jac.reset()
|
|
48
50
|
self.logger.error(f"Init error: {e}\n{traceback.format_exc()}")
|
|
49
51
|
|
|
52
|
+
async def webhook_exec(
|
|
53
|
+
self,
|
|
54
|
+
agent_id: str,
|
|
55
|
+
key: str,
|
|
56
|
+
namespace: str,
|
|
57
|
+
action: str,
|
|
58
|
+
walker: str,
|
|
59
|
+
request: Request,
|
|
60
|
+
) -> JSONResponse:
|
|
61
|
+
"""Trigger webhook execution - async compatible"""
|
|
62
|
+
try:
|
|
63
|
+
|
|
64
|
+
if not self._jac.is_valid():
|
|
65
|
+
self.logger.warning(
|
|
66
|
+
"Invalid API state for webhook, attempting to reinstate it..."
|
|
67
|
+
)
|
|
68
|
+
self._jac._authenticate()
|
|
69
|
+
|
|
70
|
+
header = dict(request.headers)
|
|
71
|
+
try:
|
|
72
|
+
payload = await request.json()
|
|
73
|
+
if not payload:
|
|
74
|
+
payload = {}
|
|
75
|
+
except Exception:
|
|
76
|
+
payload = {}
|
|
77
|
+
|
|
78
|
+
walker_obj = await self._jac.spawn_walker_async(
|
|
79
|
+
walker_name=walker,
|
|
80
|
+
module_name=f"actions.{namespace}.{action}.{walker}",
|
|
81
|
+
attributes={
|
|
82
|
+
"agent_id": agent_id,
|
|
83
|
+
"key": key,
|
|
84
|
+
"header": header,
|
|
85
|
+
"payload": payload,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
if not walker_obj:
|
|
89
|
+
self.logger.error("Webhook execution failed")
|
|
90
|
+
return JSONResponse(
|
|
91
|
+
content={"error": "Webhook execution failed"}, status_code=500
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
result = walker_obj.response
|
|
95
|
+
return JSONResponse(
|
|
96
|
+
status_code=result.get("status", 200),
|
|
97
|
+
content=result.get("message", "200 OK"),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
self._jac.reset()
|
|
102
|
+
self.logger.error(f"Webhook callback error: {e}\n{traceback.format_exc()}")
|
|
103
|
+
return JSONResponse(
|
|
104
|
+
content={"error": "Internal server error"}, status_code=500
|
|
105
|
+
)
|
|
106
|
+
|
|
50
107
|
def api_pulse(self, action_label: str, agent_id: str) -> dict:
|
|
51
108
|
"""Synchronous pulse API call"""
|
|
52
109
|
if not self._jac.is_valid():
|
jvserve/lib/jac_interface.py
CHANGED
|
@@ -124,7 +124,7 @@ class JacInterface:
|
|
|
124
124
|
|
|
125
125
|
try:
|
|
126
126
|
if module_name not in JacMachine.list_modules():
|
|
127
|
-
self.logger.error(f"Module {module_name} not
|
|
127
|
+
self.logger.error(f"Module {module_name} not found")
|
|
128
128
|
return None
|
|
129
129
|
|
|
130
130
|
entry_node = ctx.entry_node.archetype
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jvserve
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.24
|
|
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.
|
|
@@ -9,13 +9,15 @@ Keywords: jivas
|
|
|
9
9
|
Requires-Python: >=3.12.0
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: jac-cloud
|
|
12
|
+
Requires-Dist: jac-cloud==0.2.7
|
|
13
|
+
Requires-Dist: jaclang==0.8.7
|
|
13
14
|
Requires-Dist: psutil>=7.0.0
|
|
14
|
-
Requires-Dist: pyaml
|
|
15
|
-
Requires-Dist: requests
|
|
16
|
-
Requires-Dist: aiohttp
|
|
17
|
-
Requires-Dist: schedule
|
|
18
|
-
Requires-Dist: boto3
|
|
15
|
+
Requires-Dist: pyaml==25.7.0
|
|
16
|
+
Requires-Dist: requests==2.32.5
|
|
17
|
+
Requires-Dist: aiohttp==3.13.1
|
|
18
|
+
Requires-Dist: schedule==1.2.2
|
|
19
|
+
Requires-Dist: boto3==1.40.59
|
|
20
|
+
Requires-Dist: sniffio
|
|
19
21
|
Provides-Extra: dev
|
|
20
22
|
Requires-Dist: pre-commit; extra == "dev"
|
|
21
23
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
jvserve/__init__.py,sha256=Jd0pamSDn2wGTZkNk8I9qNYTFBHp7rasdYO0_Dvad_k,245
|
|
2
|
+
jvserve/cli.py,sha256=CA1fg-pmn30gwl9V48KJ_ThuDOPfaCxZOGfAtOd4gCg,17058
|
|
3
|
+
jvserve/lib/__init__.py,sha256=cnzfSHLoTWG9Ygut2nOpDys5aPlQz-m0BSkB-nd7OMs,31
|
|
4
|
+
jvserve/lib/agent_interface.py,sha256=cO2ZgII33YU6m2zU_qTThAPoHcfg8V3dAMPFGaZAun4,5511
|
|
5
|
+
jvserve/lib/agent_pulse.py,sha256=6hBF6KQYr6Z9Mi_yoWKGfdnW7gg84kK20Slu-bLR_m8,2067
|
|
6
|
+
jvserve/lib/file_interface.py,sha256=VO9RBCtJwaBxu5eZjc57-uRbsVXXZt86wVRVq9R3KXY,6079
|
|
7
|
+
jvserve/lib/jac_interface.py,sha256=hugcHF6by6_fIFfmvW7fgRMVpKe471oEWEZEA53MtEQ,8148
|
|
8
|
+
jvserve/lib/jvlogger.py,sha256=RNiB9PHuBzTvNIQWhxoDgrDlNYA0PYm1SVpvzlqu8mE,4180
|
|
9
|
+
jvserve-2.1.24.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
+
jvserve-2.1.24.dist-info/METADATA,sha256=tZTp_lJYHpTTtsuzfIgPNuEfym9dzx_SlCcQivwudtY,4873
|
|
11
|
+
jvserve-2.1.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
jvserve-2.1.24.dist-info/entry_points.txt,sha256=HYyg1QXoLs0JRb004L300VeLOZyDLY27ynD1tnTnEN4,35
|
|
13
|
+
jvserve-2.1.24.dist-info/top_level.txt,sha256=afoCXZv-zXNBuhVIvfJGjafXKEiJl_ooy4BtgQwAG4Q,8
|
|
14
|
+
jvserve-2.1.24.dist-info/RECORD,,
|
jvserve-2.1.17.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
jvserve/__init__.py,sha256=Jd0pamSDn2wGTZkNk8I9qNYTFBHp7rasdYO0_Dvad_k,245
|
|
2
|
-
jvserve/cli.py,sha256=b1AHsiJUsMVHZ7ZOKhrOJSvISPh5O5O020aoDJQJ0Rk,13910
|
|
3
|
-
jvserve/lib/__init__.py,sha256=cnzfSHLoTWG9Ygut2nOpDys5aPlQz-m0BSkB-nd7OMs,31
|
|
4
|
-
jvserve/lib/agent_interface.py,sha256=p7Fofd5q6Fk-Q900qbvPu2gB4TemPJFkOPgSq1w-oyk,3628
|
|
5
|
-
jvserve/lib/agent_pulse.py,sha256=6hBF6KQYr6Z9Mi_yoWKGfdnW7gg84kK20Slu-bLR_m8,2067
|
|
6
|
-
jvserve/lib/file_interface.py,sha256=VO9RBCtJwaBxu5eZjc57-uRbsVXXZt86wVRVq9R3KXY,6079
|
|
7
|
-
jvserve/lib/jac_interface.py,sha256=7LuY_ddG9qANgRWk7i7Oum6cX_EzB_mYAH7YYRH7ytQ,8149
|
|
8
|
-
jvserve/lib/jvlogger.py,sha256=RNiB9PHuBzTvNIQWhxoDgrDlNYA0PYm1SVpvzlqu8mE,4180
|
|
9
|
-
jvserve-2.1.17.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
-
jvserve-2.1.17.dist-info/METADATA,sha256=03dypxwccncxgYacVU4cPQ53ncrkMd4Gc9g425UmTNs,4821
|
|
11
|
-
jvserve-2.1.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
jvserve-2.1.17.dist-info/entry_points.txt,sha256=HYyg1QXoLs0JRb004L300VeLOZyDLY27ynD1tnTnEN4,35
|
|
13
|
-
jvserve-2.1.17.dist-info/top_level.txt,sha256=afoCXZv-zXNBuhVIvfJGjafXKEiJl_ooy4BtgQwAG4Q,8
|
|
14
|
-
jvserve-2.1.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|