jvserve 2.1.9__py3-none-any.whl → 2.1.10__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.
Potentially problematic release.
This version of jvserve might be problematic. Click here for more details.
- jvserve/cli.py +52 -22
- {jvserve-2.1.9.dist-info → jvserve-2.1.10.dist-info}/METADATA +1 -1
- jvserve-2.1.10.dist-info/RECORD +13 -0
- jvserve-2.1.9.dist-info/RECORD +0 -13
- {jvserve-2.1.9.dist-info → jvserve-2.1.10.dist-info}/WHEEL +0 -0
- {jvserve-2.1.9.dist-info → jvserve-2.1.10.dist-info}/entry_points.txt +0 -0
- {jvserve-2.1.9.dist-info → jvserve-2.1.10.dist-info}/licenses/LICENSE +0 -0
- {jvserve-2.1.9.dist-info → jvserve-2.1.10.dist-info}/top_level.txt +0 -0
jvserve/cli.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
import mimetypes
|
|
5
6
|
import os
|
|
6
7
|
import sys
|
|
7
8
|
import threading
|
|
@@ -14,6 +15,7 @@ from typing import AsyncIterator, Optional
|
|
|
14
15
|
import aiohttp
|
|
15
16
|
import psutil
|
|
16
17
|
import pymongo
|
|
18
|
+
import requests
|
|
17
19
|
from bson import ObjectId
|
|
18
20
|
from dotenv import load_dotenv
|
|
19
21
|
from fastapi import FastAPI, HTTPException, Response
|
|
@@ -27,6 +29,7 @@ from jac_cloud.plugin.jaseci import NodeAnchor
|
|
|
27
29
|
from jaclang import JacMachine as Jac
|
|
28
30
|
from jaclang.cli.cmdreg import cmd_registry
|
|
29
31
|
from jaclang.runtimelib.machine import hookimpl
|
|
32
|
+
from typing_extensions import Any
|
|
30
33
|
from watchfiles import Change, watch
|
|
31
34
|
|
|
32
35
|
from jvserve.lib.agent_interface import AgentInterface
|
|
@@ -67,10 +70,9 @@ async def get_url_proxy_collection() -> pymongo.collection.Collection:
|
|
|
67
70
|
return url_proxy_collection
|
|
68
71
|
|
|
69
72
|
|
|
70
|
-
async def serve_proxied_file(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"""Serve a proxied file from a remote or local URL (async version)"""
|
|
73
|
+
async def serve_proxied_file(file_path: str) -> FileResponse | StreamingResponse:
|
|
74
|
+
"""Serve a proxied file from a remote or local URL (non-blocking)"""
|
|
75
|
+
|
|
74
76
|
if FILE_INTERFACE == "local":
|
|
75
77
|
root_path = os.environ.get("JIVAS_FILES_ROOT_PATH", DEFAULT_FILES_ROOT)
|
|
76
78
|
full_path = os.path.join(root_path, file_path)
|
|
@@ -79,29 +81,57 @@ async def serve_proxied_file(
|
|
|
79
81
|
return FileResponse(full_path)
|
|
80
82
|
|
|
81
83
|
file_url = file_interface.get_file_url(file_path)
|
|
82
|
-
|
|
83
|
-
# Security check to prevent recursive calls
|
|
84
84
|
if file_url and ("localhost" in file_url or "127.0.0.1" in file_url):
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
85
|
+
# prevent recursive calls when env vars are not detected
|
|
86
|
+
raise HTTPException(status_code=500, detail="Environment not set up correctly")
|
|
88
87
|
|
|
89
88
|
if not file_url:
|
|
90
89
|
raise HTTPException(status_code=404, detail="File not found")
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
file_extension = os.path.splitext(file_path)[1].lower()
|
|
92
|
+
|
|
93
|
+
# List of extensions to serve directly
|
|
94
|
+
direct_serve_extensions = [
|
|
95
|
+
".pdf",
|
|
96
|
+
".html",
|
|
97
|
+
".txt",
|
|
98
|
+
".js",
|
|
99
|
+
".css",
|
|
100
|
+
".json",
|
|
101
|
+
".xml",
|
|
102
|
+
".svg",
|
|
103
|
+
".csv",
|
|
104
|
+
".ico",
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
if file_extension in direct_serve_extensions:
|
|
108
|
+
# Run the blocking request in a thread pool
|
|
109
|
+
file_response = await asyncio.to_thread(requests.get, file_url)
|
|
110
|
+
file_response.raise_for_status() # Raise HTTPError for bad responses (4XX or 5XX)
|
|
111
|
+
|
|
112
|
+
mime_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
|
|
113
|
+
return StreamingResponse(iter([file_response.content]), media_type=mime_type)
|
|
114
|
+
|
|
115
|
+
# For streaming responses, we need to handle it differently
|
|
116
|
+
# Get the response headers first to check if request is valid
|
|
117
|
+
file_response = await asyncio.to_thread(requests.get, file_url, stream=True)
|
|
118
|
+
file_response.raise_for_status()
|
|
119
|
+
|
|
120
|
+
# Create an async generator that reads chunks in thread pool
|
|
121
|
+
async def generate_chunks() -> Any:
|
|
122
|
+
try:
|
|
123
|
+
# Read chunks in thread pool to avoid blocking
|
|
124
|
+
for chunk in file_response.iter_content(chunk_size=1024):
|
|
125
|
+
if chunk: # filter out keep-alive chunks
|
|
126
|
+
yield chunk
|
|
127
|
+
finally:
|
|
128
|
+
# Ensure the response is closed
|
|
129
|
+
file_response.close()
|
|
130
|
+
|
|
131
|
+
return StreamingResponse(
|
|
132
|
+
generate_chunks(),
|
|
133
|
+
media_type="application/octet-stream",
|
|
134
|
+
)
|
|
105
135
|
|
|
106
136
|
|
|
107
137
|
def start_file_watcher(
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
jvserve/__init__.py,sha256=Jd0pamSDn2wGTZkNk8I9qNYTFBHp7rasdYO0_Dvad_k,245
|
|
2
|
+
jvserve/cli.py,sha256=oDzvxZ31csnUDdLPicfpihntNZ-1zm1KTI0UYC81jo0,13909
|
|
3
|
+
jvserve/lib/__init__.py,sha256=cnzfSHLoTWG9Ygut2nOpDys5aPlQz-m0BSkB-nd7OMs,31
|
|
4
|
+
jvserve/lib/agent_interface.py,sha256=Igv5Jb7i9Aq_7IbLDZ6jnldGKssAWKeb6iXoolX8u4k,3478
|
|
5
|
+
jvserve/lib/file_interface.py,sha256=VO9RBCtJwaBxu5eZjc57-uRbsVXXZt86wVRVq9R3KXY,6079
|
|
6
|
+
jvserve/lib/jac_interface.py,sha256=ydhXfYTsrhdvMXBTAd_vnAXJSSVBydQ3qavPU1-oodU,7973
|
|
7
|
+
jvserve/lib/jvlogger.py,sha256=RNiB9PHuBzTvNIQWhxoDgrDlNYA0PYm1SVpvzlqu8mE,4180
|
|
8
|
+
jvserve-2.1.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
+
jvserve-2.1.10.dist-info/METADATA,sha256=eLdSi5qrtixZVHWzvTtg9ShV-bKaGewmWELIGy7n7B4,4821
|
|
10
|
+
jvserve-2.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
jvserve-2.1.10.dist-info/entry_points.txt,sha256=HYyg1QXoLs0JRb004L300VeLOZyDLY27ynD1tnTnEN4,35
|
|
12
|
+
jvserve-2.1.10.dist-info/top_level.txt,sha256=afoCXZv-zXNBuhVIvfJGjafXKEiJl_ooy4BtgQwAG4Q,8
|
|
13
|
+
jvserve-2.1.10.dist-info/RECORD,,
|
jvserve-2.1.9.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
jvserve/__init__.py,sha256=Jd0pamSDn2wGTZkNk8I9qNYTFBHp7rasdYO0_Dvad_k,245
|
|
2
|
-
jvserve/cli.py,sha256=uQdHWJxI_Xl-hFGEanDRQWrkNJ3p3HXqt019I6e9Igk,12884
|
|
3
|
-
jvserve/lib/__init__.py,sha256=cnzfSHLoTWG9Ygut2nOpDys5aPlQz-m0BSkB-nd7OMs,31
|
|
4
|
-
jvserve/lib/agent_interface.py,sha256=Igv5Jb7i9Aq_7IbLDZ6jnldGKssAWKeb6iXoolX8u4k,3478
|
|
5
|
-
jvserve/lib/file_interface.py,sha256=VO9RBCtJwaBxu5eZjc57-uRbsVXXZt86wVRVq9R3KXY,6079
|
|
6
|
-
jvserve/lib/jac_interface.py,sha256=ydhXfYTsrhdvMXBTAd_vnAXJSSVBydQ3qavPU1-oodU,7973
|
|
7
|
-
jvserve/lib/jvlogger.py,sha256=RNiB9PHuBzTvNIQWhxoDgrDlNYA0PYm1SVpvzlqu8mE,4180
|
|
8
|
-
jvserve-2.1.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
-
jvserve-2.1.9.dist-info/METADATA,sha256=n1eA5e-0jJohwUgaQ6SQnw_iwZN7kHHEjlbI3YUFc4k,4820
|
|
10
|
-
jvserve-2.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
-
jvserve-2.1.9.dist-info/entry_points.txt,sha256=HYyg1QXoLs0JRb004L300VeLOZyDLY27ynD1tnTnEN4,35
|
|
12
|
-
jvserve-2.1.9.dist-info/top_level.txt,sha256=afoCXZv-zXNBuhVIvfJGjafXKEiJl_ooy4BtgQwAG4Q,8
|
|
13
|
-
jvserve-2.1.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|