jvserve 2.1.9__tar.gz → 2.1.11__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jvserve
3
- Version: 2.1.9
3
+ Version: 2.1.11
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.
@@ -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
- file_path: str,
72
- ) -> FileResponse | StreamingResponse | Response:
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
- raise HTTPException(
86
- status_code=500, detail="Environment misconfiguration detected"
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
- try:
93
- async with aiohttp.ClientSession() as session:
94
- async with session.get(file_url) as response:
95
- response.raise_for_status()
96
-
97
- return StreamingResponse(
98
- response.content.iter_chunked(8192),
99
- media_type=response.headers.get(
100
- "Content-Type", "application/octet-stream"
101
- ),
102
- )
103
- except aiohttp.ClientError as e:
104
- raise HTTPException(status_code=502, detail=f"File fetch error: {str(e)}")
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(
@@ -122,7 +152,6 @@ def start_file_watcher(
122
152
  # Kill the current server process and restart
123
153
  reload_jivas()
124
154
  else:
125
- jvlogger.info("Watcher disabled, ignoring changes")
126
155
  time.sleep(1) # Prevent busy loop when disabled
127
156
  except KeyboardInterrupt:
128
157
  jvlogger.info("File watcher stopped")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jvserve
3
- Version: 2.1.9
3
+ Version: 2.1.11
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.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes