mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-22 10:17:13 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf5170ea52 | |||
| 1f1a3aff55 | |||
| de28b3160c | |||
| 79773b669a |
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:22-alpine AS node
|
||||
FROM --platform=$BUILDPLATFORM node:22-alpine AS node
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### One-Click Cloud Deploy
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -83,6 +83,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### 一键云部署
|
||||
|
||||
[](https://zeabur.com/zh-CN/templates/ZKTBDH)
|
||||
|
||||
@@ -82,6 +82,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### Despliegue en la Nube con un Clic
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -82,6 +82,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### Déploiement Cloud en un Clic
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -82,6 +82,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### ワンクリッククラウドデプロイ
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -82,6 +82,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### 원클릭 클라우드 배포
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -82,6 +82,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### Облачное развертывание одним кликом
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -84,6 +84,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### 一鍵雲端部署
|
||||
|
||||
[](https://zeabur.com/zh-CN/templates/ZKTBDH)
|
||||
|
||||
@@ -82,6 +82,7 @@ cd LangBot/docker
|
||||
docker compose --profile all up -d
|
||||
```
|
||||
|
||||
|
||||
### Triển khai đám mây một cú nhấp
|
||||
|
||||
[](https://zeabur.com/en-US/templates/ZKTBDH)
|
||||
|
||||
@@ -47,11 +47,10 @@ services:
|
||||
restart: on-failure
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
# Shared control-plane secret used to authenticate both the RPC socket
|
||||
# and managed-process relay. Generate once (for example with
|
||||
# ``openssl rand -hex 32``) and export it before enabling this profile.
|
||||
# An empty value is accepted by Compose so Box can remain optional, but
|
||||
# the Box runtime itself fails closed when the profile is started.
|
||||
# Optional shared control-plane secret used to authenticate both the RPC
|
||||
# socket and managed-process relay. Leave unset on both OSS services, or
|
||||
# generate one with ``openssl rand -hex 32`` and set the same value on
|
||||
# both ends. Strongly recommended when the deployment is Internet-accessible.
|
||||
- LANGBOT_BOX_CONTROL_TOKEN=${LANGBOT_BOX_CONTROL_TOKEN:-}
|
||||
# Box has its own process-wide blocking-work budget.
|
||||
- LANGBOT_BLOCKING_EXECUTOR_MAX_WORKERS=${LANGBOT_BLOCKING_EXECUTOR_MAX_WORKERS:-8}
|
||||
@@ -79,8 +78,9 @@ services:
|
||||
- TZ=Asia/Shanghai
|
||||
# Optional. Leave unset on both OSS services, or match plugin Runtime.
|
||||
- LANGBOT_PLUGIN_RUNTIME_CONTROL_TOKEN=${LANGBOT_PLUGIN_RUNTIME_CONTROL_TOKEN:-}
|
||||
# Must match the value supplied to langbot_box. The token is sent only
|
||||
# in WebSocket handshake headers, never in URLs or action payloads.
|
||||
# When set, this must match langbot_box. If both ends leave it unset,
|
||||
# OSS permits the connection without token authentication. The token is
|
||||
# sent only in WebSocket handshake headers, never in URLs or payloads.
|
||||
- LANGBOT_BOX_CONTROL_TOKEN=${LANGBOT_BOX_CONTROL_TOKEN:-}
|
||||
# Core process-wide blocking-work admission. These are native config
|
||||
# overrides and are persisted with the effective data/config.yaml.
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ dependencies = [
|
||||
"chromadb>=1.0.0,<2.0.0",
|
||||
"qdrant-client (>=1.15.1,<2.0.0)",
|
||||
"pyseekdb==1.1.0.post3",
|
||||
"langbot-plugin @ git+https://github.com/langbot-app/langbot-plugin-sdk.git@555a58e5db3de28e977b08dd4cd116b332848a19",
|
||||
"langbot-plugin==0.5.3",
|
||||
"asyncpg>=0.30.0",
|
||||
"line-bot-sdk>=3.19.0",
|
||||
"matrix-nio>=0.25.2",
|
||||
|
||||
@@ -14,11 +14,6 @@ from ...service.user import ControlPlaneDirectoryRequiredError, PublicRegistrati
|
||||
|
||||
@group.group_class('user', '/api/v1/user')
|
||||
class UserRouterGroup(group.RouterGroup):
|
||||
@staticmethod
|
||||
def _is_loopback_host(hostname: str) -> bool:
|
||||
normalized = hostname.casefold().rstrip('.')
|
||||
return normalized in {'localhost', '127.0.0.1', '::1'}
|
||||
|
||||
def _validate_space_redirect_uri(self, redirect_uri: str, *, bind: bool) -> str:
|
||||
parsed = urlsplit(redirect_uri)
|
||||
if (
|
||||
@@ -38,10 +33,6 @@ class UserRouterGroup(group.RouterGroup):
|
||||
elif query:
|
||||
raise ValueError('Invalid LangBot Account login redirect_uri')
|
||||
|
||||
# OSS instances can live behind arbitrary domains and gateway ports.
|
||||
# Accept any HTTPS callback, plus HTTP only for local development.
|
||||
if parsed.scheme == 'http' and not self._is_loopback_host(parsed.hostname):
|
||||
raise ValueError('Insecure redirect_uri origin')
|
||||
return redirect_uri
|
||||
|
||||
async def initialize(self) -> None:
|
||||
|
||||
@@ -165,7 +165,7 @@ async def test_bind_state_is_account_bound_and_requires_authentication(space_oau
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_redirect_allows_dynamic_https_origin_and_loopback_http(space_oauth_api):
|
||||
async def test_redirect_allows_any_http_or_https_origin(space_oauth_api):
|
||||
_, client = space_oauth_api
|
||||
|
||||
responses = [
|
||||
@@ -181,6 +181,8 @@ async def test_redirect_allows_dynamic_https_origin_and_loopback_http(space_oaut
|
||||
'http://localhost:5300/auth/space/callback',
|
||||
'http://127.0.0.1:5300/auth/space/callback',
|
||||
'http://[::1]:5300/auth/space/callback',
|
||||
'http://langbot.example/auth/space/callback',
|
||||
'http://192.0.2.10:5300/auth/space/callback',
|
||||
)
|
||||
]
|
||||
|
||||
@@ -190,7 +192,7 @@ async def test_redirect_allows_dynamic_https_origin_and_loopback_http(space_oaut
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_redirect_rejects_insecure_remote_origin_and_invalid_callback_shape(space_oauth_api):
|
||||
async def test_redirect_rejects_invalid_callback_shape(space_oauth_api):
|
||||
_, client = space_oauth_api
|
||||
|
||||
responses = [
|
||||
@@ -199,7 +201,6 @@ async def test_redirect_rejects_insecure_remote_origin_and_invalid_callback_shap
|
||||
query_string={'redirect_uri': redirect_uri},
|
||||
)
|
||||
for redirect_uri in (
|
||||
'http://langbot.example/auth/space/callback',
|
||||
'https://langbot.example/arbitrary',
|
||||
'https://langbot.example/auth/space/callback?next=https://evil.example',
|
||||
'https://user@langbot.example/auth/space/callback',
|
||||
|
||||
@@ -1018,7 +1018,7 @@ name = "cuda-bindings"
|
||||
version = "13.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cuda-pathfinder", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "cuda-pathfinder" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539, upload-time = "2026-05-29T23:11:43.19Z" },
|
||||
@@ -1051,34 +1051,34 @@ wheels = [
|
||||
|
||||
[package.optional-dependencies]
|
||||
cudart = [
|
||||
{ name = "nvidia-cuda-runtime", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cuda-runtime" },
|
||||
]
|
||||
cufft = [
|
||||
{ name = "nvidia-cufft", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cufft" },
|
||||
]
|
||||
cufile = [
|
||||
{ name = "nvidia-cufile", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cufile" },
|
||||
]
|
||||
cupti = [
|
||||
{ name = "nvidia-cuda-cupti", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cuda-cupti" },
|
||||
]
|
||||
curand = [
|
||||
{ name = "nvidia-curand", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-curand" },
|
||||
]
|
||||
cusolver = [
|
||||
{ name = "nvidia-cusolver", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cusolver" },
|
||||
]
|
||||
cusparse = [
|
||||
{ name = "nvidia-cusparse", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cusparse" },
|
||||
]
|
||||
nvjitlink = [
|
||||
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
nvrtc = [
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cuda-nvrtc" },
|
||||
]
|
||||
nvtx = [
|
||||
{ name = "nvidia-nvtx", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nvtx" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2125,7 +2125,7 @@ requires-dist = [
|
||||
{ name = "ebooklib", specifier = ">=0.18" },
|
||||
{ name = "gewechat-client", specifier = ">=0.1.5" },
|
||||
{ name = "html2text", specifier = ">=2024.2.26" },
|
||||
{ name = "langbot-plugin", git = "https://github.com/langbot-app/langbot-plugin-sdk.git?rev=555a58e5db3de28e977b08dd4cd116b332848a19" },
|
||||
{ name = "langbot-plugin", specifier = "==0.5.3" },
|
||||
{ name = "langchain", specifier = ">=1.3.9" },
|
||||
{ name = "langchain-core", specifier = ">=1.3.3" },
|
||||
{ name = "langchain-text-splitters", specifier = ">=1.1.2" },
|
||||
@@ -2192,7 +2192,7 @@ dev = [
|
||||
[[package]]
|
||||
name = "langbot-plugin"
|
||||
version = "0.5.3"
|
||||
source = { git = "https://github.com/langbot-app/langbot-plugin-sdk.git?rev=555a58e5db3de28e977b08dd4cd116b332848a19#555a58e5db3de28e977b08dd4cd116b332848a19" }
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
{ name = "aiohttp" },
|
||||
@@ -2212,6 +2212,10 @@ dependencies = [
|
||||
{ name = "watchdog" },
|
||||
{ name = "websockets" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/55/1d/a54daa3bc699f5186b9970946c2ecf0e9cf219f77738934e04aaf5a0c20a/langbot_plugin-0.5.3.tar.gz", hash = "sha256:2324b1f7e1f55e3692e75c8b1e427ea497474b0150ec6ca83b49b5d77ec224c6", size = 472149, upload-time = "2026-08-13T10:17:45.529Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/5f/ae6ed59773cc9d941fbb28b4ac07ce2a29e689d693e29ad71c40c5b39aa5/langbot_plugin-0.5.3-py3-none-any.whl", hash = "sha256:75dea1b6fb79ec6087ec3284f6698fb701d5feebf5f0318a7ae51fbd17a2f41f", size = 304559, upload-time = "2026-08-13T10:17:44.385Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "langchain"
|
||||
@@ -3238,7 +3242,7 @@ name = "nvidia-cublas"
|
||||
version = "13.1.1.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-cuda-nvrtc" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" },
|
||||
@@ -3277,7 +3281,7 @@ name = "nvidia-cudnn-cu13"
|
||||
version = "9.20.0.48"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-cublas" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" },
|
||||
@@ -3289,7 +3293,7 @@ name = "nvidia-cufft"
|
||||
version = "12.0.0.61"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
|
||||
@@ -3319,9 +3323,9 @@ name = "nvidia-cusolver"
|
||||
version = "12.0.4.66"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-cusparse", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-cublas" },
|
||||
{ name = "nvidia-cusparse" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
|
||||
@@ -3333,7 +3337,7 @@ name = "nvidia-cusparse"
|
||||
version = "12.6.3.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
|
||||
@@ -5163,10 +5167,10 @@ name = "scikit-learn"
|
||||
version = "1.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "joblib", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "numpy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "scipy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "threadpoolctl", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "joblib" },
|
||||
{ name = "numpy" },
|
||||
{ name = "scipy" },
|
||||
{ name = "threadpoolctl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
|
||||
wheels = [
|
||||
@@ -5213,7 +5217,7 @@ name = "scipy"
|
||||
version = "1.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "numpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
||||
wheels = [
|
||||
@@ -5284,14 +5288,14 @@ name = "sentence-transformers"
|
||||
version = "5.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "huggingface-hub", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "numpy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "scikit-learn", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "scipy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "torch", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "tqdm", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "transformers", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "numpy" },
|
||||
{ name = "scikit-learn" },
|
||||
{ name = "scipy" },
|
||||
{ name = "torch" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "transformers" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5b/30/21664028fc0776eb1ca024879480bbbab36f02923a8ff9e4cae5a150fa35/sentence_transformers-5.2.3.tar.gz", hash = "sha256:3cd3044e1f3fe859b6a1b66336aac502eaae5d3dd7d5c8fc237f37fbf58137c7", size = 381623, upload-time = "2026-02-17T14:05:20.238Z" }
|
||||
wheels = [
|
||||
@@ -5664,21 +5668,21 @@ name = "torch"
|
||||
version = "2.12.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cuda-bindings", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "filelock", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "fsspec", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "jinja2", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "networkx", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cudnn-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cusparselt-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nccl-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nvshmem-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "setuptools", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "sympy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "triton", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "cuda-bindings", marker = "sys_platform == 'linux'" },
|
||||
{ name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" },
|
||||
{ name = "filelock" },
|
||||
{ name = "fsspec" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "networkx" },
|
||||
{ name = "nvidia-cublas", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" },
|
||||
{ name = "setuptools" },
|
||||
{ name = "sympy" },
|
||||
{ name = "triton", marker = "sys_platform == 'linux'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/38/7028d3be540f1dcdf41660a2b01d0c51d2cb73915fe370d84e4d277a6d47/torch-2.12.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ef81f503912effea2ce3d9b12a2e3a6ed488943e91271c90c7a829f60baf6aa2", size = 87975425, upload-time = "2026-06-17T21:08:34.094Z" },
|
||||
@@ -5720,15 +5724,15 @@ name = "transformers"
|
||||
version = "5.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "huggingface-hub", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "numpy", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "packaging", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "pyyaml", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "regex", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "safetensors", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "tokenizers", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "tqdm", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "typer", marker = "python_full_version >= '3.14'" },
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "numpy" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "regex" },
|
||||
{ name = "safetensors" },
|
||||
{ name = "tokenizers" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typer" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" }
|
||||
wheels = [
|
||||
@@ -5989,9 +5993,9 @@ name = "valkey-glide"
|
||||
version = "2.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio", marker = "sys_platform != 'win32'" },
|
||||
{ name = "protobuf", marker = "sys_platform != 'win32'" },
|
||||
{ name = "sniffio", marker = "sys_platform != 'win32'" },
|
||||
{ name = "anyio" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "sniffio" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/a2/582b34c6acc8dc857c537f6007459cba48dfa0dc404789a657e5c1a998c0/valkey_glide-2.4.1.tar.gz", hash = "sha256:f1155d84156d11b90488aa67e90102f0bf98a45314f5b99308ac9074c05f7241", size = 898030, upload-time = "2026-05-28T21:41:55.881Z" }
|
||||
wheels = [
|
||||
|
||||
@@ -710,11 +710,32 @@ export class BackendClient extends BaseHttpClient {
|
||||
);
|
||||
}
|
||||
|
||||
private async getAuthenticatedObjectURL(path: string): Promise<string> {
|
||||
private async getAuthenticatedObjectURL(
|
||||
path: string,
|
||||
rewritePluginPageSdk = false,
|
||||
): Promise<string> {
|
||||
const response = await this.instance.get<Blob>(path, {
|
||||
responseType: 'blob',
|
||||
});
|
||||
return URL.createObjectURL(response.data);
|
||||
let blob = response.data;
|
||||
if (rewritePluginPageSdk && blob.type.startsWith('text/html')) {
|
||||
const apiBase =
|
||||
this.instance.defaults.baseURL === '/'
|
||||
? window.location.origin
|
||||
: this.instance.defaults.baseURL?.replace(/\/$/, '');
|
||||
const pageSdkUrl = `${apiBase}/api/v1/plugins/_sdk/page-sdk.js`;
|
||||
const html = await blob.text();
|
||||
blob = new Blob(
|
||||
[
|
||||
html.replace(
|
||||
/(<script\b[^>]*\bsrc\s*=\s*)(["'])\/api\/v1\/plugins\/_sdk\/page-sdk\.js\2/gi,
|
||||
`$1$2${pageSdkUrl}$2`,
|
||||
),
|
||||
],
|
||||
{ type: blob.type },
|
||||
);
|
||||
}
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
public getAuthenticatedPluginAssetURL(
|
||||
@@ -724,6 +745,7 @@ export class BackendClient extends BaseHttpClient {
|
||||
): Promise<string> {
|
||||
return this.getAuthenticatedObjectURL(
|
||||
`/api/v1/plugins/${author}/${name}/authenticated-assets/${filepath}`,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,55 @@ test('loads a Cloud plugin page through the authenticated asset route', async ({
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: '<!doctype html><html><body><h1>LangRAG Observability</h1></body></html>',
|
||||
body: `<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>LangRAG Observability</h1>
|
||||
<button id="save">Save</button>
|
||||
<script src="/api/v1/plugins/_sdk/page-sdk.js"></script>
|
||||
<script>
|
||||
document.querySelector('#save').addEventListener('click', async () => {
|
||||
await window.langbot.api('/settings', { enabled: true }, 'POST');
|
||||
document.body.dataset.saved = 'true';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>`,
|
||||
});
|
||||
},
|
||||
);
|
||||
let pageSdkRequests = 0;
|
||||
await page.route('**/api/v1/plugins/_sdk/page-sdk.js', async (route) => {
|
||||
pageSdkRequests += 1;
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/javascript',
|
||||
body: `window.langbot = {
|
||||
api(endpoint, body, method) {
|
||||
return new Promise((resolve) => {
|
||||
const requestId = 'request-' + Date.now();
|
||||
const handler = (event) => {
|
||||
if (event.data?.type === 'langbot:api:response' && event.data.requestId === requestId) {
|
||||
window.removeEventListener('message', handler);
|
||||
resolve(event.data.data);
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', handler);
|
||||
window.parent.postMessage({ type: 'langbot:api', requestId, endpoint, body, method }, '*');
|
||||
});
|
||||
},
|
||||
};`,
|
||||
});
|
||||
});
|
||||
let pageApiRequests = 0;
|
||||
await page.route(
|
||||
'**/api/v1/plugins/langbot-team/LangRAG/page-api',
|
||||
async (route) => {
|
||||
pageApiRequests += 1;
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: wrapped({ saved: true }),
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -78,11 +126,17 @@ test('loads a Cloud plugin page through the authenticated asset route', async ({
|
||||
'/home/plugin-pages?id=langbot-team%2FLangRAG%2Fobservability',
|
||||
);
|
||||
|
||||
const pluginFrame = page.frameLocator('iframe');
|
||||
await expect(
|
||||
page
|
||||
.frameLocator('iframe')
|
||||
.getByRole('heading', { name: 'LangRAG Observability' }),
|
||||
pluginFrame.getByRole('heading', { name: 'LangRAG Observability' }),
|
||||
).toBeVisible();
|
||||
await pluginFrame.getByRole('button', { name: 'Save' }).click();
|
||||
await expect(pluginFrame.locator('body')).toHaveAttribute(
|
||||
'data-saved',
|
||||
'true',
|
||||
);
|
||||
expect(authenticatedAssetRequests).toBeGreaterThan(0);
|
||||
expect(pageSdkRequests).toBe(1);
|
||||
expect(pageApiRequests).toBe(1);
|
||||
await expect(page.getByText('Loading...')).toHaveCount(0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user