Files
Javier Martinez 21d42fd97a feat: code execution v4 (#2295)
* feat: add bundle to remove

* fix: spaces

* feat: add xml render as skill spec

* feat: add skill volume root to cache

* fix: deduplicate values

* fix: change the mount path to skill_id

* fix: use different paths

* feat: add principal

(cherry picked from commit 5db64fe721d5706440ce9f342b1388ffe742bc16)

# Conflicts:
#	private_gpt/components/code_execution/base.py
#	private_gpt/components/code_execution/code_execution_component.py
#	private_gpt/components/code_execution/local.py
#	private_gpt/components/environment/manager.py
#	private_gpt/components/tools/builders/bash_tool_builder.py
#	private_gpt/components/tools/builders/text_editor_tool_builder.py
#	private_gpt/components/tools/processors/bash_processor.py

* fix: mypy

...

* fix: config

* fix: sandbox config

* feat: add forward cookies

* fix: loop

* feat: add present server

* feat: add feature flag for tools

* refactor: move principal to another better place

* feat: add api key principal

* docs: fix docs

* docs: add present server

* fix: principal

* fix: mypy

* fix: avoid to block the loop

* fix: blocks in expansion

* fix: remove maximum concurrent users

...

* fix: multiplexer

* fix: readers

* fix: more fixes

...

* fix: impl

* feat: tool scheduler

* feat: add adaptative

* feat: add chat worker

* fix: config

* fix: max

* feat: add chat/tools workers

* fix: mypy

* feat: add generic scheduler

* fix: get result

* feat: do serializable the tool executor

* fix: tools

* fix: config

* fix: config

* fix: args

* fix: config

* fix: serializer

* Revert "fix: blocks in expansion"

This reverts commit a2110f94a8.

* fix: unify all logic

* feat: add ingestion scheduler

* fix: settings

* fix: config

* feat: add arq worker to chat

* fix: arq worker

* fix: add nest

* fix: mypy

* fix: await

* fix: script stress

* fix: tokenizer

* fix: chat scheduler

* fix: mypy

* fix: add async tokenizer

* fix: improve condense

* fix: tool scheduler

* feat: add initial real async chat worker

* fix: mypy

* fix: do resumable local executor

...

...

...

fix: revert usleess changes

fix: remove parent chat job

fix: refactor

fix: loop

ref: rename models

fix: chat engine

fix: mypy

...

...

...

fix: fix deps

* fix: tests

* fix: tests

* ...

* fix: stream

* fix: config

* fix: scheduler

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Handle PGPT_WORKER_MODE=celery in health check worker status

* fix: cancel

* fix: arch

* fix: test ingestion

* fix: deserialization of chat messages

* fix: broken results

* fix: mypy

* fix: test

* fix: config

* fix: remove arq tool worker

* fix: output cls

* fix: preserve early resumable tool callbacks

* fix: preserve async tool result order

* refactor: address worker PR review comments

* fix: mypy

* test: colocate ARQ chat enqueue coverage

* fix: remove redis from tests

* fix: mypy

* fix: tests

* test: isolate chat mocks and cancellation timing

* fix: tests

* fix: tests

* fix: test

(cherry picked from commit f8ee460af2)

* fix: worker config

* test: remove flaky chat cancellation assertion

(cherry picked from commit 1115ff2349)

# Conflicts:
#	tests/server/chat/test_chat_routes.py

* fix: emit chat pings from stream listeners

* fix: don't duplciate the output

* fix: rss memory

...

* fix: pass the args

* fix: threads

* fix: websearch

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-15 13:05:23 +02:00

96 lines
3.8 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from injector import inject
from private_gpt.components.code_execution.base import CodeExecutionProvider
from private_gpt.components.code_execution.sandbox_session import (
SandboxCodeExecutionSession,
)
from private_gpt.components.environment.content_mounter import (
FetchContentMounter,
InlineContentMounter,
LocalStorageContentMounter,
)
from private_gpt.components.environment.manager import EnvironmentManager
from private_gpt.components.environment.mounter import LocalDirMounter
from private_gpt.components.sandbox.local import LocalSandboxProvider
from private_gpt.settings.settings import Settings
if TYPE_CHECKING:
from private_gpt.components.code_execution.base import (
CodeExecutionSession,
CodeExecutionSessionConfig,
)
from private_gpt.components.environment.content_mounter import ContentMounter
from private_gpt.components.environment.mounter import LayoutMounter
class LocalCodeExecutionProvider(CodeExecutionProvider):
"""Code execution tool over locally managed environments.
A thin adapter: the EnvironmentManager owns session lifecycle, the
LocalDirMounter owns the host directories (which survive sandbox
restarts), and the local sandbox provider owns execution.
"""
@inject
def __init__(self, settings: Settings) -> None:
super().__init__(settings)
base = Path(
settings.code_execution.workspace_path
or Path(settings.data.local_data_folder) / "code_execution_workspaces"
)
self._manager = EnvironmentManager(
sandbox_provider=LocalSandboxProvider(settings),
layout_mounter=self._make_layout_mounter(base),
content_mounters=self._make_content_mounters(),
ttl_seconds=settings.code_execution.session_ttl_seconds,
)
def _make_layout_mounter(self, base: Path) -> LayoutMounter:
"""Factory hook — subclasses override to inject cloud-backed storage.
When volume_root is set (Files API enabled), sessions are rooted there
so that files uploaded via the Files API are accessible to the sandbox
at the same host paths where LocalObjectStorage writes them.
"""
volume_root = self.settings.code_execution.volume_root
if volume_root is not None:
return LocalDirMounter(Path(volume_root))
return LocalDirMounter(base)
def _make_content_mounters(self) -> list[ContentMounter]:
"""Build the ordered content-mounter list for this deployment.
When skills are stored locally, LocalStorageContentMounter provides a
direct host-path volume (no fetch needed). FetchContentMounter is the
universal fallback for any StoredBundle. InlineContentMounter handles
plain ContentBundle instances whose files are already in memory.
"""
mounters: list[ContentMounter] = []
if self.settings.skills.storage_provider == "local":
storage_root = Path(self.settings.data.local_data_folder) / "storage"
mounters.append(LocalStorageContentMounter(storage_root))
mounters.append(FetchContentMounter())
mounters.append(InlineContentMounter())
return mounters
async def create_session(
self,
config: CodeExecutionSessionConfig,
) -> SandboxCodeExecutionSession:
env = await self._manager.acquire(
config.session_id,
config.extra_bundles or None,
config.bundles_to_remove or None,
config.env or None,
)
return SandboxCodeExecutionSession(env)
def delete_session(self, session: CodeExecutionSession) -> None:
if isinstance(session, SandboxCodeExecutionSession):
self._manager.release(session._id)