jvserve 2.0.10__tar.gz → 2.0.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jvserve
3
- Version: 2.0.10
3
+ Version: 2.0.12
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.
@@ -4,5 +4,5 @@ jvserve package initialization.
4
4
  This package provides the webserver for loading and interacting with JIVAS agents.
5
5
  """
6
6
 
7
- __version__ = "2.0.10"
7
+ __version__ = "2.0.12"
8
8
  __supported__jivas__versions__ = ["2.0.0"]
@@ -7,20 +7,80 @@ from contextlib import asynccontextmanager
7
7
  from typing import AsyncIterator, Optional
8
8
 
9
9
  from dotenv import load_dotenv
10
+ from fastapi.responses import FileResponse, StreamingResponse
10
11
  from jac_cloud.jaseci.security import authenticator
12
+ from jac_cloud.plugin.jaseci import NodeAnchor
11
13
  from jaclang.cli.cmdreg import cmd_registry
12
14
  from jaclang.plugin.default import hookimpl
13
15
  from jaclang.runtimelib.context import ExecutionContext
14
16
  from jaclang.runtimelib.machine import JacMachine
17
+ from requests import Response
15
18
  from uvicorn import run as _run
16
19
 
17
20
  from jvserve.lib.agent_interface import AgentInterface
18
21
  from jvserve.lib.agent_pulse import AgentPulse
22
+ from jvserve.lib.file_interface import (
23
+ DEFAULT_FILES_ROOT,
24
+ FILE_INTERFACE,
25
+ file_interface,
26
+ )
19
27
  from jvserve.lib.jvlogger import JVLogger
20
28
 
21
29
  load_dotenv(".env")
22
30
 
23
31
 
32
+ def serve_proxied_file(file_path: str) -> FileResponse | StreamingResponse:
33
+ """Serve a proxied file from a remote or local URL."""
34
+ import mimetypes
35
+
36
+ import requests
37
+ from fastapi import HTTPException
38
+
39
+ if FILE_INTERFACE == "local":
40
+ return FileResponse(path=os.path.join(DEFAULT_FILES_ROOT, file_path))
41
+
42
+ file_url = file_interface.get_file_url(file_path)
43
+
44
+ if file_url and ("localhost" in file_url or "127.0.0.1" in file_url):
45
+ # prevent recusive calls when env vars are not detected
46
+ raise HTTPException(status_code=500, detail="Environment not set up correctly")
47
+
48
+ if not file_url:
49
+ raise HTTPException(status_code=404, detail="File not found")
50
+
51
+ file_extension = os.path.splitext(file_path)[1].lower()
52
+
53
+ # List of extensions to serve directly
54
+ direct_serve_extensions = [
55
+ ".pdf",
56
+ ".html",
57
+ ".txt",
58
+ ".js",
59
+ ".css",
60
+ ".json",
61
+ ".xml",
62
+ ".svg",
63
+ ".csv",
64
+ ".ico",
65
+ ]
66
+
67
+ if file_extension in direct_serve_extensions:
68
+ file_response = requests.get(file_url)
69
+ file_response.raise_for_status() # Raise HTTPError for bad responses (4XX or 5XX)
70
+
71
+ mime_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
72
+
73
+ return StreamingResponse(iter([file_response.content]), media_type=mime_type)
74
+
75
+ file_response = requests.get(file_url, stream=True)
76
+ file_response.raise_for_status()
77
+
78
+ return StreamingResponse(
79
+ file_response.iter_content(chunk_size=1024),
80
+ media_type="application/octet-stream",
81
+ )
82
+
83
+
24
84
  class JacCmd:
25
85
  """Jac CLI."""
26
86
 
@@ -116,18 +176,12 @@ class JacCmd:
116
176
  def jvfileserve(
117
177
  directory: str, host: str = "0.0.0.0", port: int = 9000
118
178
  ) -> None:
119
- """Launch the file server."""
179
+ """Launch the file server for local files."""
120
180
  # load FastAPI
121
181
  from fastapi import FastAPI
122
182
  from fastapi.middleware.cors import CORSMiddleware
123
183
  from fastapi.staticfiles import StaticFiles
124
184
 
125
- if directory:
126
- os.environ["JIVAS_FILES_ROOT_PATH"] = directory
127
-
128
- if not os.path.exists(directory):
129
- os.makedirs(directory)
130
-
131
185
  # Setup custom routes
132
186
  app = FastAPI()
133
187
 
@@ -140,21 +194,68 @@ class JacCmd:
140
194
  allow_headers=["*"],
141
195
  )
142
196
 
197
+ if not os.path.exists(directory):
198
+ os.makedirs(directory)
199
+
200
+ # Set the environment variable for the file root path
201
+ os.environ["JIVAS_FILES_ROOT_PATH"] = directory
202
+
203
+ # Mount the static files directory
143
204
  app.mount(
144
205
  "/files",
145
- StaticFiles(
146
- directory=os.environ.get("JIVAS_FILES_ROOT_PATH", ".files")
147
- ),
206
+ StaticFiles(directory=directory),
148
207
  name="files",
149
208
  )
150
209
 
151
- app.mount(
152
- "/files",
153
- StaticFiles(
154
- directory=os.environ.get("JIVAS_FILES_ROOT_PATH", ".files")
155
- ),
156
- name="files",
210
+ # run the app
211
+ _run(app, host=host, port=port)
212
+
213
+ @cmd_registry.register
214
+ def jvproxyserve(
215
+ directory: str, host: str = "0.0.0.0", port: int = 9000
216
+ ) -> None:
217
+ """Launch the file proxy server for remote files."""
218
+ # load FastAPI
219
+ from bson import ObjectId
220
+ from fastapi import FastAPI, HTTPException
221
+ from fastapi.middleware.cors import CORSMiddleware
222
+
223
+ # Setup custom routes
224
+ app = FastAPI()
225
+
226
+ # Add CORS middleware
227
+ app.add_middleware(
228
+ CORSMiddleware,
229
+ allow_origins=["*"],
230
+ allow_credentials=True,
231
+ allow_methods=["*"],
232
+ allow_headers=["*"],
157
233
  )
158
234
 
235
+ # Add proxy routes only if using S3
236
+ if FILE_INTERFACE == "s3":
237
+
238
+ @app.get("/files/{file_path:path}", response_model=None)
239
+ async def serve_file(
240
+ file_path: str,
241
+ ) -> Response:
242
+ return serve_proxied_file(file_path)
243
+
244
+ @app.get("/f/{file_id:path}", response_model=None)
245
+ async def get_proxied_file(
246
+ file_id: str,
247
+ ) -> Response:
248
+ params = file_id.split("/")
249
+ object_id = params[0]
250
+
251
+ # mongo db collection
252
+ collection = NodeAnchor.Collection.get_collection("url_proxies")
253
+ file_details = collection.find_one({"_id": ObjectId(object_id)})
254
+
255
+ if file_details:
256
+ return serve_proxied_file(file_details["path"])
257
+
258
+ raise HTTPException(status_code=404, detail="File not found")
259
+
159
260
  # run the app
160
261
  _run(app, host=host, port=port)
@@ -7,6 +7,10 @@ import logging
7
7
  import os
8
8
  from abc import ABC, abstractmethod
9
9
 
10
+ from dotenv import load_dotenv
11
+
12
+ load_dotenv(".env")
13
+
10
14
  # Interface type determined by environment variable, defaults to local
11
15
  FILE_INTERFACE = os.environ.get("JIVAS_FILE_INTERFACE", "local")
12
16
  DEFAULT_FILES_ROOT = os.environ.get("JIVAS_FILES_ROOT_PATH", ".files")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jvserve
3
- Version: 2.0.10
3
+ Version: 2.0.12
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.
@@ -118,6 +118,26 @@ class JVServeCliTest(unittest.TestCase):
118
118
  os.remove(f"{directory}/test.txt")
119
119
  os.rmdir(directory)
120
120
 
121
+ def test_jvproxyserve_runs(self) -> None:
122
+ """Ensure `jac jvproxyserve` runs successfully."""
123
+ try:
124
+ directory = "test_files"
125
+ server_process = subprocess.Popen(
126
+ ["jac", "jvproxyserve", directory, "--port", "9100"],
127
+ stdout=subprocess.PIPE,
128
+ stderr=subprocess.PIPE,
129
+ text=True,
130
+ )
131
+
132
+ # Wait for the proxy server to be ready
133
+ self.wait_for_server("http://127.0.0.1:9100/docs")
134
+
135
+ res = httpx.get("http://127.0.0.1:9100/docs")
136
+ self.assertEqual(res.status_code, 200)
137
+
138
+ finally:
139
+ server_process.kill()
140
+
121
141
  def tearDown(self) -> None:
122
142
  """Cleanup after each test."""
123
143
  self.stop_server()
File without changes
File without changes
File without changes
File without changes