From ed9c6b0316b60dfa8d67f04b0953f39ad72f9187 Mon Sep 17 00:00:00 2001 From: Davide Silvestri <75379892+silvestrid@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:31:12 +0100 Subject: [PATCH 1/4] fix(assistant): resolve UDSPY env vars pollution (#5030) * fix(assistant): resolve UDSPY credentials at model creation instead of polluting os.environ The UDSPY_LM_API_KEY and UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL env vars were being bridged via os.environ.setdefault() at startup, which leaked into the AI field's credential resolution. Move credential fallback into _resolve_model() using a dispatch-dict of per-provider factories that read env vars on demand without ever writing to os.environ. * test(assistant): add tests for credential resolution and env-var precedence Covers _resolve_credentials and _resolve_model: provider-specific env vars take precedence over UDSPY_LM_* compat fallback, unknown providers still get the fallback, and resolution never mutates os.environ. --- .../assistant/model_profiles.py | 4 +- .../assistant/retrying_model.py | 165 ++++++++++++++++-- .../assistant/tools/search_user_docs/tools.py | 5 +- .../config/settings/settings.py | 22 +-- .../assistant/test_retrying_model.py | 115 ++++++++++++ 5 files changed, 273 insertions(+), 38 deletions(-) diff --git a/enterprise/backend/src/baserow_enterprise/assistant/model_profiles.py b/enterprise/backend/src/baserow_enterprise/assistant/model_profiles.py index 25f19a625b..34796cd77d 100644 --- a/enterprise/backend/src/baserow_enterprise/assistant/model_profiles.py +++ b/enterprise/backend/src/baserow_enterprise/assistant/model_profiles.py @@ -163,12 +163,14 @@ def get_model_string(model: str | None = None) -> str: @lru_cache(maxsize=1) def check_lm_ready_or_raise() -> None: + from baserow_enterprise.assistant.retrying_model import _resolve_model + model = get_model_string() test_agent = Agent( output_type=str, instructions="Respond with 'ok'.", name="test_agent" ) try: - test_agent.run_sync("Test", model=model) + test_agent.run_sync("Test", model=_resolve_model(model)) except Exception as e: raise AssistantModelNotSupportedError( f"The model '{model}' is not supported or accessible: {e}" diff --git a/enterprise/backend/src/baserow_enterprise/assistant/retrying_model.py b/enterprise/backend/src/baserow_enterprise/assistant/retrying_model.py index 007f55398d..395934f24a 100644 --- a/enterprise/backend/src/baserow_enterprise/assistant/retrying_model.py +++ b/enterprise/backend/src/baserow_enterprise/assistant/retrying_model.py @@ -33,8 +33,9 @@ import asyncio import json +import os import re -from collections.abc import AsyncIterator +from collections.abc import AsyncIterator, Callable from contextlib import asynccontextmanager from datetime import datetime, timezone from typing import Any @@ -172,29 +173,159 @@ def _try_recover_tool_use_failed(exc: Exception) -> ModelResponse | None: return _recover_failed_generation(failed_gen, model_name) +# --------------------------------------------------------------------------- +# Provider credential resolution +# --------------------------------------------------------------------------- +# Maps provider prefixes to their native env-var names. +# +# Backward-compat: when a provider-specific var is not set we fall back to +# the deprecated UDSPY_LM_* vars so existing deployments keep working. +# This compat layer is intentionally minimal — new providers should NOT be +# added here; operators should use the standard env vars instead. + +_PROVIDER_ENV: dict[str, dict[str, str | None]] = { + "openai": { + "api_key": "OPENAI_API_KEY", + "base_url": "OPENAI_BASE_URL", + }, + "groq": { + "api_key": "GROQ_API_KEY", + }, + "anthropic": { + "api_key": "ANTHROPIC_API_KEY", + }, + "ollama": { + "base_url": "OLLAMA_BASE_URL", + }, +} + + +def _resolve_credentials(provider: str) -> dict[str, str | None]: + """Return ``{"api_key": ..., "base_url": ...}`` for *provider*. + + Checks the provider-specific env var first, then falls back to the + deprecated ``UDSPY_LM_API_KEY`` / ``UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL`` + for backward-compat. Never touches ``os.environ``. + """ + + env = _PROVIDER_ENV.get(provider, {}) + api_key_var = env.get("api_key") + base_url_var = env.get("base_url") + + api_key = ( + (os.getenv(api_key_var) if api_key_var else None) + or os.getenv("UDSPY_LM_API_KEY") + or None + ) + base_url = ( + (os.getenv(base_url_var) if base_url_var else None) + or os.getenv("UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL") + or None + ) + return {"api_key": api_key, "base_url": base_url} + + +# --------------------------------------------------------------------------- +# Per-provider model factories +# --------------------------------------------------------------------------- + + +def _make_openai(name: str, creds: dict[str, str | None]) -> Model: + from pydantic_ai.models.openai import OpenAIChatModel + from pydantic_ai.providers.openai import OpenAIProvider + + kwargs = {k: v for k, v in creds.items() if v is not None} + return OpenAIChatModel(name, provider=OpenAIProvider(**kwargs)) + + +def _make_groq(name: str, creds: dict[str, str | None]) -> Model: + from pydantic_ai.models.groq import GroqModel + from pydantic_ai.providers.groq import GroqProvider + + return GroqModel(name, provider=GroqProvider(api_key=creds["api_key"])) + + +def _make_anthropic(name: str, creds: dict[str, str | None]) -> Model: + from pydantic_ai.models.anthropic import AnthropicModel + from pydantic_ai.providers.anthropic import AnthropicProvider + + return AnthropicModel(name, provider=AnthropicProvider(api_key=creds["api_key"])) + + +def _make_ollama(name: str, creds: dict[str, str | None]) -> Model: + from pydantic_ai.models.openai import OpenAIChatModel + from pydantic_ai.providers.ollama import OllamaProvider + + base_url = creds["base_url"] or "http://localhost:11434/v1" + return OpenAIChatModel(name, provider=OllamaProvider(base_url=base_url)) + + +def _make_google(name: str, creds: dict[str, str | None]) -> Model: + """Google models need a fresh httpx client per call to avoid event-loop + binding issues in Django async views. + See: https://github.com/pydantic/pydantic-ai/issues/3240 + """ + + import httpx + from pydantic_ai.models.google import GoogleModel + from pydantic_ai.providers.google import GoogleProvider + + return GoogleModel( + name, + provider=GoogleProvider( + api_key=creds["api_key"], http_client=httpx.AsyncClient() + ), + ) + + +def _make_google_vertex(name: str, creds: dict[str, str | None]) -> Model: + import httpx + from pydantic_ai.models.google import GoogleModel + from pydantic_ai.providers.google import GoogleProvider + + return GoogleModel( + name, + provider=GoogleProvider( + api_key=creds["api_key"], + http_client=httpx.AsyncClient(), + vertexai=True, + ), + ) + + +_PROVIDER_FACTORIES: dict[str, Callable[[str, dict[str, str | None]], Model]] = { + "openai": _make_openai, + "groq": _make_groq, + "anthropic": _make_anthropic, + "ollama": _make_ollama, + "google-gla": _make_google, + "google": _make_google, + "google-vertex": _make_google_vertex, +} + + +# --------------------------------------------------------------------------- +# Model resolution +# --------------------------------------------------------------------------- + + def _resolve_model(model_name: str) -> Model: """Resolve a model name to a pydantic-ai Model instance. - For Google models, constructs the model with a fresh - ``httpx.AsyncClient`` instead of relying on ``infer_model()`` which - uses a process-global cached client. That cached client binds to the - event loop at creation time and breaks when reused on a different loop - (common in Django async views). - See: https://github.com/pydantic/pydantic-ai/issues/3240 + Uses explicit provider construction with credential fallback to + ``UDSPY_LM_API_KEY`` / ``UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL`` + so we never need to set ``os.environ``. """ - if model_name.startswith(("google-gla:", "google:", "google-vertex:")): - import httpx - from pydantic_ai.models.google import GoogleModel - from pydantic_ai.providers.google import GoogleProvider + provider = model_name.split(":")[0] if ":" in model_name else "openai" + name = model_name.split(":", 1)[1] if ":" in model_name else model_name - vertexai = model_name.startswith("google-vertex:") - google_model_name = model_name.split(":", 1)[1] - return GoogleModel( - google_model_name, - provider=GoogleProvider(http_client=httpx.AsyncClient(), vertexai=vertexai), - ) + factory = _PROVIDER_FACTORIES.get(provider) + if factory is not None: + creds = _resolve_credentials(provider) + return factory(name, creds) + # Unknown provider — let pydantic-ai handle it. return infer_model(model_name) diff --git a/enterprise/backend/src/baserow_enterprise/assistant/tools/search_user_docs/tools.py b/enterprise/backend/src/baserow_enterprise/assistant/tools/search_user_docs/tools.py index bb1eba26f9..79d360ff52 100644 --- a/enterprise/backend/src/baserow_enterprise/assistant/tools/search_user_docs/tools.py +++ b/enterprise/backend/src/baserow_enterprise/assistant/tools/search_user_docs/tools.py @@ -199,8 +199,11 @@ def _search(question: str) -> list[KnowledgeBaseChunk]: f"Documentation context (source URL -> content):\n{context}" ) from baserow_enterprise.assistant.model_profiles import get_model_string + from baserow_enterprise.assistant.retrying_model import _resolve_model - agent_result = await search_docs_agent.run(prompt, model=get_model_string()) + agent_result = await search_docs_agent.run( + prompt, model=_resolve_model(get_model_string()) + ) prediction = agent_result.output sources = [] diff --git a/enterprise/backend/src/baserow_enterprise/config/settings/settings.py b/enterprise/backend/src/baserow_enterprise/config/settings/settings.py index 595288e713..338d73e23b 100644 --- a/enterprise/backend/src/baserow_enterprise/config/settings/settings.py +++ b/enterprise/backend/src/baserow_enterprise/config/settings/settings.py @@ -84,29 +84,13 @@ def setup(settings): float(_temp_raw) if _temp_raw else None ) - # Backward compatibility: bridge old UDSPY_LM_* env vars so existing - # deployments continue to work without config changes. + # Backward compatibility: bridge old UDSPY_LM_MODEL to the new setting. + # Credential fallback (UDSPY_LM_API_KEY, UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL) + # is handled at model-creation time in retrying_model._resolve_model(). _udspy_model = os.getenv("UDSPY_LM_MODEL", "") if _udspy_model and not settings.BASEROW_ENTERPRISE_ASSISTANT_LLM_MODEL: settings.BASEROW_ENTERPRISE_ASSISTANT_LLM_MODEL = _udspy_model - _udspy_api_key = os.getenv("UDSPY_LM_API_KEY", "") - if _udspy_api_key: - # pydantic-ai reads provider-specific env vars. Set them all as - # fallbacks so the old catch-all key works regardless of provider. - for _key in ( - "OPENAI_API_KEY", - "GROQ_API_KEY", - "ANTHROPIC_API_KEY", - "GEMINI_API_KEY", - ): - os.environ.setdefault(_key, _udspy_api_key) - - _udspy_base_url = os.getenv("UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL", "") - if _udspy_base_url: - # pydantic-ai's OpenAI provider reads OPENAI_BASE_URL. - os.environ.setdefault("OPENAI_BASE_URL", _udspy_base_url) - # Bridge old AWS_REGION_NAME to boto3's standard AWS_DEFAULT_REGION. _aws_region = os.getenv("AWS_REGION_NAME", "") if _aws_region: diff --git a/enterprise/backend/tests/baserow_enterprise_tests/assistant/test_retrying_model.py b/enterprise/backend/tests/baserow_enterprise_tests/assistant/test_retrying_model.py index c83caf73ef..5b3f4f8140 100644 --- a/enterprise/backend/tests/baserow_enterprise_tests/assistant/test_retrying_model.py +++ b/enterprise/backend/tests/baserow_enterprise_tests/assistant/test_retrying_model.py @@ -1,10 +1,14 @@ """Unit tests for RetryingModel.""" +import os + import pytest from baserow_enterprise.assistant.retrying_model import ( RetryingModel, _is_transient_provider_error, + _resolve_credentials, + _resolve_model, ) @@ -470,3 +474,114 @@ async def stream_that_fails_during_consumption(*args, **kwargs): [], None, ModelRequestParameters(function_tools=[], output_tools=[]) ) as stream: pass # stream consumed, then __aexit__ raises + + +# --------------------------------------------------------------------------- +# Credential resolution & model dispatch +# --------------------------------------------------------------------------- + + +class TestResolveCredentials: + """Tests for _resolve_credentials env-var precedence.""" + + def test_provider_specific_key_takes_precedence(self, monkeypatch): + monkeypatch.setenv("GROQ_API_KEY", "groq-key") + monkeypatch.setenv("UDSPY_LM_API_KEY", "udspy-key") + + creds = _resolve_credentials("groq") + assert creds["api_key"] == "groq-key" + + def test_falls_back_to_udspy_api_key(self, monkeypatch): + monkeypatch.delenv("GROQ_API_KEY", raising=False) + monkeypatch.setenv("UDSPY_LM_API_KEY", "udspy-key") + + creds = _resolve_credentials("groq") + assert creds["api_key"] == "udspy-key" + + def test_provider_specific_base_url_takes_precedence(self, monkeypatch): + monkeypatch.setenv("OPENAI_BASE_URL", "https://custom.openai.com") + monkeypatch.setenv("UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL", "https://udspy.com") + + creds = _resolve_credentials("openai") + assert creds["base_url"] == "https://custom.openai.com" + + def test_falls_back_to_udspy_base_url(self, monkeypatch): + monkeypatch.delenv("OPENAI_BASE_URL", raising=False) + monkeypatch.setenv("UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL", "https://udspy.com") + + creds = _resolve_credentials("openai") + assert creds["base_url"] == "https://udspy.com" + + def test_returns_none_when_nothing_set(self, monkeypatch): + for var in ( + "OPENAI_API_KEY", + "OPENAI_BASE_URL", + "UDSPY_LM_API_KEY", + "UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL", + ): + monkeypatch.delenv(var, raising=False) + + creds = _resolve_credentials("openai") + assert creds["api_key"] is None + assert creds["base_url"] is None + + def test_unknown_provider_still_gets_udspy_fallback(self, monkeypatch): + monkeypatch.setenv("UDSPY_LM_API_KEY", "udspy-key") + + creds = _resolve_credentials("some_unknown_provider") + assert creds["api_key"] == "udspy-key" + + def test_never_mutates_os_environ(self, monkeypatch): + monkeypatch.setenv("UDSPY_LM_API_KEY", "udspy-key") + monkeypatch.setenv("UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL", "https://udspy.com") + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + monkeypatch.delenv("GROQ_API_KEY", raising=False) + + snapshot = dict(os.environ) + + _resolve_credentials("openai") + _resolve_credentials("groq") + _resolve_credentials("anthropic") + + assert dict(os.environ) == snapshot + + +class TestResolveModel: + """Tests for _resolve_model provider dispatch.""" + + def test_openai_prefix(self, monkeypatch): + monkeypatch.setenv("OPENAI_API_KEY", "sk-test") + from pydantic_ai.models.openai import OpenAIChatModel + + model = _resolve_model("openai:gpt-4o") + assert isinstance(model, OpenAIChatModel) + + def test_groq_prefix(self, monkeypatch): + monkeypatch.setenv("GROQ_API_KEY", "gsk-test") + from pydantic_ai.models.groq import GroqModel + + model = _resolve_model("groq:llama-3") + assert isinstance(model, GroqModel) + + def test_bare_model_defaults_to_openai(self, monkeypatch): + monkeypatch.setenv("OPENAI_API_KEY", "sk-test") + from pydantic_ai.models.openai import OpenAIChatModel + + model = _resolve_model("gpt-4o") + assert isinstance(model, OpenAIChatModel) + + def test_ollama_uses_default_base_url(self, monkeypatch): + monkeypatch.delenv("OLLAMA_BASE_URL", raising=False) + monkeypatch.delenv("UDSPY_LM_OPENAI_COMPATIBLE_BASE_URL", raising=False) + from pydantic_ai.models.openai import OpenAIChatModel + + model = _resolve_model("ollama:llama2") + assert isinstance(model, OpenAIChatModel) + + def test_never_mutates_os_environ(self, monkeypatch): + monkeypatch.setenv("UDSPY_LM_API_KEY", "udspy-key") + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + + snapshot = dict(os.environ) + _resolve_model("openai:gpt-4o") + assert dict(os.environ) == snapshot From 7cb44da43189807aaca56c962c89f3e19fd4447f Mon Sep 17 00:00:00 2001 From: Davide Silvestri <75379892+silvestrid@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:54:05 +0100 Subject: [PATCH 2/4] chore(frontend): ignore .claude dir in vite watcher and fix Nitro EMFILE (#5038) - Add .claude/ to vite server watch ignore list alongside node_modules and .git to avoid unnecessary file watching in worktrees. - Configure Nitro devStorage to use fs-lite driver to prevent chokidar from watching the entire repo root, which causes EMFILE on macOS in large monorepos. --- web-frontend/config/nuxt.config.base.ts | 8 ++++++++ web-frontend/config/nuxt.config.dev.ts | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/web-frontend/config/nuxt.config.base.ts b/web-frontend/config/nuxt.config.base.ts index 94b53a21da..d8f74296a7 100644 --- a/web-frontend/config/nuxt.config.base.ts +++ b/web-frontend/config/nuxt.config.base.ts @@ -98,6 +98,14 @@ export default defineNuxtConfig({ }, server: { sourcemapIgnoreList: (sourcePath) => sourcePath.includes('node_modules'), + watch: { + ignored: [ + '**/node_modules/**', + '**/.git/**', + '**/.nuxt/**', + '**/.claude/**', + ], + }, }, optimizeDeps: { // Pre-bundle moment-guess to avoid missing source map warning diff --git a/web-frontend/config/nuxt.config.dev.ts b/web-frontend/config/nuxt.config.dev.ts index 04cd608c75..b1dd891625 100644 --- a/web-frontend/config/nuxt.config.dev.ts +++ b/web-frontend/config/nuxt.config.dev.ts @@ -5,4 +5,17 @@ export default defineNuxtConfig({ ...baseConfig, modules: [...(baseConfig.modules || []), '@nuxt/eslint'], devtools: { enabled: true }, + hooks: { + // Prevent Nitro's devStorage from watching the entire repo root with + // chokidar, which causes EMFILE on macOS in large monorepos / worktrees. + // See https://github.com/nuxt/nuxt/issues/30481 + 'nitro:config'(nitroConfig) { + nitroConfig.devStorage ??= {} + nitroConfig.devStorage['root'] = { + driver: 'fs-lite', + readOnly: true, + base: nitroConfig.rootDir, + } + }, + }, }) From 6a15ddc7824da1d64399278240cb64fd6a6c969a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:38:52 +0100 Subject: [PATCH 3/4] chore(deps): bump requests from 2.32.5 to 2.33.0 in /backend (#5054) Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/pyproject.toml | 2 +- backend/uv.lock | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 2f50270b68..6a8ea85066 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "gunicorn==23.0.0", "uvicorn[standard]==0.40.0", "websockets==15.0.1", - "requests==2.32.5", + "requests==2.33.0", "itsdangerous==2.2.0", "Pillow==12.1.1", "drf-spectacular==0.29.0", diff --git a/backend/uv.lock b/backend/uv.lock index ee367b6e29..1c33e12710 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -396,7 +396,7 @@ requires-dist = [ { name = "qrcode", specifier = "==8.2" }, { name = "redis", specifier = "==6.4.0" }, { name = "regex", specifier = "==2025.11.3" }, - { name = "requests", specifier = "==2.32.5" }, + { name = "requests", specifier = "==2.33.0" }, { name = "requests-futures", specifier = ">=1.0.2" }, { name = "requests-oauthlib", specifier = "==2.0.0" }, { name = "rich", specifier = "==14.3.2" }, @@ -1435,6 +1435,7 @@ wheels = [ name = "griffelib" version = "2.0.0" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/06/eccbd311c9e2b3ca45dbc063b93134c57a1ccc7607c5e545264ad092c4a9/griffelib-2.0.0.tar.gz", hash = "sha256:e504d637a089f5cab9b5daf18f7645970509bf4f53eda8d79ed71cce8bd97934", size = 166312, upload-time = "2026-03-23T21:06:55.954Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" }, ] @@ -3435,7 +3436,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, @@ -3443,9 +3444,9 @@ dependencies = [ { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] From be97ea4a57be3bcf6828d8bcf0cb6f97901954b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:39:37 +0100 Subject: [PATCH 4/4] chore(deps): bump h3 from 1.15.8 to 1.15.10 in /web-frontend (#5037) Bumps [h3](https://github.com/h3js/h3) from 1.15.8 to 1.15.10. - [Release notes](https://github.com/h3js/h3/releases) - [Changelog](https://github.com/h3js/h3/blob/v1.15.10/CHANGELOG.md) - [Commits](https://github.com/h3js/h3/compare/v1.15.8...v1.15.10) --- updated-dependencies: - dependency-name: h3 dependency-version: 1.15.10 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web-frontend/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web-frontend/yarn.lock b/web-frontend/yarn.lock index f88bb16753..5a481a6b28 100644 --- a/web-frontend/yarn.lock +++ b/web-frontend/yarn.lock @@ -6984,9 +6984,9 @@ gzip-size@^7.0.0: srvx "^0.10.0" h3@^1.12.0, h3@^1.15.4: - version "1.15.8" - resolved "https://registry.yarnpkg.com/h3/-/h3-1.15.8.tgz#bf90969bc1141a88be0940b7290f8353456b9f2a" - integrity sha512-iOH6Vl8mGd9nNfu9C0IZ+GuOAfJHcyf3VriQxWaSWIB76Fg4BnFuk4cxBxjmQSSxJS664+pgjP6e7VBnUzFfcg== + version "1.15.10" + resolved "https://registry.yarnpkg.com/h3/-/h3-1.15.10.tgz#defe650df7b70cf585d2020c4146fb580cfb0d42" + integrity sha512-YzJeWSkDZxAhvmp8dexjRK5hxziRO7I9m0N53WhvYL5NiWfkUkzssVzY9jvGu0HBoLFW6+duYmNSn6MaZBCCtg== dependencies: cookie-es "^1.2.2" crossws "^0.3.5"