Skip to content

feat: sync Computer Use outputs into Open WebUI files#83

Open
belugaming wants to merge 1 commit intoYambr:mainfrom
belugaming:feat/owui-output-sync
Open

feat: sync Computer Use outputs into Open WebUI files#83
belugaming wants to merge 1 commit intoYambr:mainfrom
belugaming:feat/owui-output-sync

Conversation

@belugaming
Copy link
Copy Markdown

@belugaming belugaming commented Apr 28, 2026

Summary

  • sync final Computer Use outputs into Open WebUI's native file store and surface /api/v1/files/<id>/content references in tool results
  • update prompt guidance so final deliverables prefer Open WebUI-relative file paths when output sync is configured
  • make compose host data path and bind host configurable, and add coverage for sync behavior plus Dockerfile packaging

Test plan

  • python3 -m pytest /Users/beluga/open-computer-use/tests/orchestrator/test_owui_sync.py -v
  • python3 -m py_compile /Users/beluga/open-computer-use/computer-use-server/owui_sync.py /Users/beluga/open-computer-use/computer-use-server/system_prompt.py /Users/beluga/open-computer-use/computer-use-server/mcp_tools.py /Users/beluga/open-computer-use/computer-use-server/docker_manager.py
  • docker compose -f /Users/beluga/open-computer-use/docker-compose.yml up -d --force-recreate computer-use-server cleanup
  • curl -fsS http://127.0.0.1:8081/health
  • manual smoke test: synced a temporary output file into Open WebUI and received /api/v1/files/<id>/content

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Open WebUI integration for automatic output synchronization with intelligent file caching.
    • Configurable network binding and data directory location options.
  • Documentation

    • Updated environment configuration with new Open WebUI sync settings.
  • Tests

    • Added comprehensive tests for output synchronization and file upload functionality.

Sync final outputs into Open WebUI's native file store and surface relative /api/v1/files/<id>/content paths so public Open WebUI deployments no longer depend on Computer Use file URLs. Make the host data path and bind host configurable for compose users, and add coverage for sync behavior and Dockerfile packaging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR integrates Open WebUI-native output synchronization for the computer-use server. It replaces external token provider configuration with new OWUI settings, introduces a new sync module that uploads outputs to Open WebUI with MD5-based manifest caching, integrates sync calls into MCP tools via background threading, and updates system prompts to guide agent file path preferences. Docker and environment configurations are updated to support the feature.

Changes

Cohort / File(s) Summary
Configuration & Environment
.env.example, docker-compose.yml
Replaces legacy MCP token provider settings with OWUI configuration (OWUI_INTERNAL_URL, OWUI_API_KEY); adds configurable host binding (MCP_BIND_HOST) and data directory (HOST_COMPUTER_USE_DATA_DIR) with templated defaults.
Docker Setup
computer-use-server/Dockerfile
Copies new owui_sync.py module into container /app directory for runtime availability.
Core Configuration & Sync Module
computer-use-server/docker_manager.py, computer-use-server/owui_sync.py
Reads OWUI environment variables; introduces standalone sync implementation with MD5-based manifest caching, multipart file uploads to Open WebUI /api/v1/files/ endpoint, and output formatting helper.
Integration into MCP Tools
computer-use-server/mcp_tools.py, computer-use-server/system_prompt.py
Decorates bash_tool, str_replace, create_file, and sub_agent to call optional background sync when configured; augments system prompt with conditional guidance to prefer Open WebUI file paths in results.
Tests
tests/orchestrator/test_mcp_tools.py, tests/orchestrator/test_owui_sync.py
Adds integration test for tool sync behavior and comprehensive end-to-end test for sync module, including manifest idempotency, upload error handling, and prompt validation.

Sequence Diagram

sequenceDiagram
    participant Tool as MCP Tool<br/>(bash_tool, etc.)
    participant Sync as Sync Module<br/>(owui_sync.py)
    participant FS as File System<br/>(outputs/, manifest)
    participant OWUI as Open WebUI<br/>Server

    Tool->>Sync: _sync_outputs_if_configured(chat_id)
    Sync->>FS: Check if outputs dir exists
    FS-->>Sync: Directory found
    Sync->>FS: Walk files, compute MD5
    FS-->>Sync: Files + hashes
    Sync->>FS: Load .owui_sync_manifest.json
    FS-->>Sync: Manifest data
    Sync->>Sync: Check MD5 against manifest
    alt File unchanged
        Sync-->>Sync: Reuse cached file_id
    else File new or changed
        Sync->>OWUI: POST /api/v1/files/ (multipart)
        OWUI-->>Sync: file_id, content_url
        Sync->>FS: Update manifest with new entry
    end
    Sync->>FS: Write updated manifest
    Sync-->>Tool: Return synced_files metadata
    Tool->>Tool: format_sync_summary(output, synced_files)
    Tool-->>User: Formatted result + file links
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A-hop through the webs we go,
Files bundled up in sync's soft glow,
MD5 hashes guard the way,
WebUI pathways come out to play! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically summarizes the main change: syncing Computer Use outputs into Open WebUI files, which is the primary objective across the entire changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
computer-use-server/system_prompt.py (1)

707-712: Avoid exact full-line text matching for prompt patching.

Line 709’s literal match is fragile; small wording/whitespace edits will silently drop the OWUI guidance. Prefer inserting the note at a structural anchor (for example before </sharing_files>) with a single replacement.

♻️ Suggested robust insertion pattern
-    if OWUI_INTERNAL_URL:
-        result = result.replace(
-            "   - Action: Copy completed files here and share as HTTP links",
-            "   - Action: Copy completed files here and share as HTTP links\n"
-            "   - If tool results include Open WebUI file paths like /api/v1/files/<id>/content, prefer the Open WebUI file paths returned in tool results for final deliverables",
-        )
+    if OWUI_INTERNAL_URL:
+        marker = "</sharing_files>"
+        owui_note = (
+            "- If tool results include Open WebUI file paths like /api/v1/files/<id>/content, "
+            "prefer the Open WebUI file paths returned in tool results for final deliverables\n"
+        )
+        result = result.replace(marker, f"{owui_note}{marker}", 1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/system_prompt.py` around lines 707 - 712, The current
fragile replacement uses a full-line literal match inside the result.replace
call when OWUI_INTERNAL_URL is set; change it to perform a single, structural
insertion before the closing sharing_files tag (e.g., locate the string
"</sharing_files>" and insert the OWUI guidance immediately before it) instead
of matching the exact line; update the code that references OWUI_INTERNAL_URL
and result.replace to build the guidance text and call a single replace or
insertion using the "</sharing_files>" anchor so minor wording/whitespace
changes won't break the patch.
computer-use-server/owui_sync.py (1)

100-103: Don’t swallow upload failures silently.

Catching Exception here makes sync failures invisible, so partial or complete upload outages look like a successful run. At minimum, log the file path and exception before continuing.

🪵 Suggested fix
+import logging
+
+logger = logging.getLogger(__name__)
+
 def _upload_file(owui_url: str, owui_api_key: str, file_path: Path) -> tuple[str, str]:
@@
         try:
             file_id, url = _upload_file(owui_url, owui_api_key, file_path)
-        except Exception:
+        except Exception:
+            logger.exception("Failed to sync %s to Open WebUI", file_path)
             continue
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/owui_sync.py` around lines 100 - 103, The try/except
around the call to _upload_file is swallowing failures; change the except
Exception: block to except Exception as e: and log the failure (including
file_path and the exception) before continuing — e.g., use the module logger
(logging.getLogger(__name__)) or the existing logger in this module to call
logger.exception or logger.error with file_path and e; keep the continue after
logging so the loop behavior is unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@computer-use-server/owui_sync.py`:
- Around line 19-20: The manifest path helper _manifest_path currently ignores
chat_id so manifests are shared across chats; update _manifest_path to accept a
chat_id (e.g., def _manifest_path(outputs_dir: Path, chat_id: str) -> Path) and
use chat_id to scope the manifest filename or subdirectory (for example include
chat_id in the filename or create outputs_dir / chat_id / _MANIFEST_NAME) to
ensure per-chat isolation; also apply the same change to the other locations
referenced (the cached file-ID read/write logic around the block noted "69-76")
so all file-ID manifests are created/loaded per chat_id rather than globally.
- Around line 84-112: The manifest and results currently use file_path.name,
which conflates files with the same basename; change to use the relative path
from outputs_dir as the manifest key and as the filename in results (e.g. key =
file_path.relative_to(outputs_dir).as_posix()). Update the lookup of manifest
(replace manifest.get(file_path.name) with manifest.get(key)), store the entry
under that key, and append results entries using that relative path; keep the
existing use of _md5(file_path) and _upload_file(owui_url, owui_api_key,
file_path) and preserve the rest of the logic (updated flag, synced_at, file_id,
url).

In `@tests/orchestrator/test_owui_sync.py`:
- Around line 210-217: The test may import a previously cached system_prompt
module so the patched fake_skill_manager/fake_docker_manager get ignored; before
importing, remove "system_prompt" from sys.modules (or use importlib.reload) to
force a fresh import, then import and clear system_prompt_module._render_cache
and call system_prompt_module.render_system_prompt_sync("abc123", None); update
the test around system_prompt_module to ensure the patched modules are used
deterministically.

---

Nitpick comments:
In `@computer-use-server/owui_sync.py`:
- Around line 100-103: The try/except around the call to _upload_file is
swallowing failures; change the except Exception: block to except Exception as
e: and log the failure (including file_path and the exception) before continuing
— e.g., use the module logger (logging.getLogger(__name__)) or the existing
logger in this module to call logger.exception or logger.error with file_path
and e; keep the continue after logging so the loop behavior is unchanged.

In `@computer-use-server/system_prompt.py`:
- Around line 707-712: The current fragile replacement uses a full-line literal
match inside the result.replace call when OWUI_INTERNAL_URL is set; change it to
perform a single, structural insertion before the closing sharing_files tag
(e.g., locate the string "</sharing_files>" and insert the OWUI guidance
immediately before it) instead of matching the exact line; update the code that
references OWUI_INTERNAL_URL and result.replace to build the guidance text and
call a single replace or insertion using the "</sharing_files>" anchor so minor
wording/whitespace changes won't break the patch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a166b79-5b0e-4660-bb14-6ee973c2c628

📥 Commits

Reviewing files that changed from the base of the PR and between 141756c and 1801fee.

📒 Files selected for processing (9)
  • .env.example
  • computer-use-server/Dockerfile
  • computer-use-server/docker_manager.py
  • computer-use-server/mcp_tools.py
  • computer-use-server/owui_sync.py
  • computer-use-server/system_prompt.py
  • docker-compose.yml
  • tests/orchestrator/test_mcp_tools.py
  • tests/orchestrator/test_owui_sync.py

Comment on lines +19 to +20
def _manifest_path(outputs_dir: Path) -> Path:
return outputs_dir / _MANIFEST_NAME
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep the manifest scoped to a chat.

chat_id is currently discarded, so every chat sharing the same outputs_dir will reuse the same manifest and cached file IDs. That breaks the per-chat isolation described in the PR and can surface the wrong Open WebUI file in a later chat.

🔧 Suggested fix
-def _manifest_path(outputs_dir: Path) -> Path:
-    return outputs_dir / _MANIFEST_NAME
+def _manifest_path(outputs_dir: Path, chat_id: str) -> Path:
+    return outputs_dir / chat_id / _MANIFEST_NAME
@@
 def sync_outputs_to_owui(
     chat_id: str,
@@
 ) -> List[dict]:
-    del chat_id
     outputs_dir = Path(outputs_dir)

Also applies to: 69-76

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/owui_sync.py` around lines 19 - 20, The manifest path
helper _manifest_path currently ignores chat_id so manifests are shared across
chats; update _manifest_path to accept a chat_id (e.g., def
_manifest_path(outputs_dir: Path, chat_id: str) -> Path) and use chat_id to
scope the manifest filename or subdirectory (for example include chat_id in the
filename or create outputs_dir / chat_id / _MANIFEST_NAME) to ensure per-chat
isolation; also apply the same change to the other locations referenced (the
cached file-ID read/write logic around the block noted "69-76") so all file-ID
manifests are created/loaded per chat_id rather than globally.

Comment on lines +84 to +112
for file_path in sorted(outputs_dir.rglob("*")):
if not file_path.is_file() or file_path.name.startswith("."):
continue

digest = _md5(file_path)
entry = manifest.get(file_path.name)
if entry and entry.get("md5") == digest and entry.get("file_id"):
results.append(
{
"filename": file_path.name,
"file_id": entry["file_id"],
"url": entry["url"],
}
)
continue

try:
file_id, url = _upload_file(owui_url, owui_api_key, file_path)
except Exception:
continue

manifest[file_path.name] = {
"md5": digest,
"file_id": file_id,
"url": url,
"synced_at": time.time(),
}
updated = True
results.append({"filename": file_path.name, "file_id": file_id, "url": url})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the relative path as the manifest key.

Keying the manifest and result list by file_path.name collapses distinct files that share a basename in different subdirectories. That can reuse the wrong file_id/content URL for a different file.

🔧 Suggested fix
     for file_path in sorted(outputs_dir.rglob("*")):
         if not file_path.is_file() or file_path.name.startswith("."):
             continue
 
         digest = _md5(file_path)
-        entry = manifest.get(file_path.name)
+        relative_name = str(file_path.relative_to(outputs_dir))
+        entry = manifest.get(relative_name)
         if entry and entry.get("md5") == digest and entry.get("file_id"):
             results.append(
                 {
-                    "filename": file_path.name,
+                    "filename": relative_name,
                     "file_id": entry["file_id"],
                     "url": entry["url"],
                 }
             )
             continue
@@
-        manifest[file_path.name] = {
+        manifest[relative_name] = {
             "md5": digest,
             "file_id": file_id,
             "url": url,
             "synced_at": time.time(),
         }
         updated = True
-        results.append({"filename": file_path.name, "file_id": file_id, "url": url})
+        results.append({"filename": relative_name, "file_id": file_id, "url": url})
🧰 Tools
🪛 Ruff (0.15.12)

[error] 102-103: try-except-continue detected, consider logging the exception

(S112)


[warning] 102-102: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@computer-use-server/owui_sync.py` around lines 84 - 112, The manifest and
results currently use file_path.name, which conflates files with the same
basename; change to use the relative path from outputs_dir as the manifest key
and as the filename in results (e.g. key =
file_path.relative_to(outputs_dir).as_posix()). Update the lookup of manifest
(replace manifest.get(file_path.name) with manifest.get(key)), store the entry
under that key, and append results entries using that relative path; keep the
existing use of _md5(file_path) and _upload_file(owui_url, owui_api_key,
file_path) and preserve the rest of the logic (updated flag, synced_at, file_id,
url).

Comment on lines +210 to +217
with patch.dict(sys.modules, {
"skill_manager": fake_skill_manager,
"docker_manager": fake_docker_manager,
}):
import importlib
system_prompt_module = importlib.import_module("system_prompt")
system_prompt_module._render_cache.clear()
body = system_prompt_module.render_system_prompt_sync("abc123", None)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make this prompt test deterministic by forcing a fresh system_prompt import.

Line 215 can return a previously cached system_prompt module, so your patched docker_manager/skill_manager may be ignored depending on test order.

✅ Deterministic import fix
         with patch.dict(sys.modules, {
             "skill_manager": fake_skill_manager,
             "docker_manager": fake_docker_manager,
         }):
             import importlib
+            sys.modules.pop("system_prompt", None)
             system_prompt_module = importlib.import_module("system_prompt")
-            system_prompt_module._render_cache.clear()
             body = system_prompt_module.render_system_prompt_sync("abc123", None)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/orchestrator/test_owui_sync.py` around lines 210 - 217, The test may
import a previously cached system_prompt module so the patched
fake_skill_manager/fake_docker_manager get ignored; before importing, remove
"system_prompt" from sys.modules (or use importlib.reload) to force a fresh
import, then import and clear system_prompt_module._render_cache and call
system_prompt_module.render_system_prompt_sync("abc123", None); update the test
around system_prompt_module to ensure the patched modules are used
deterministically.

Copy link
Copy Markdown
Owner

Yambr commented May 2, 2026

Thanks for the contribution! Before I can review this seriously I need a bit more context, and I see a number of concrete issues that need to be addressed. Could you walk me through the following?

1. Motivation — what problem does this actually solve?

The orchestrator already exposes every output file via PUBLIC_BASE_URL/files/<chat_id>/<filename> (and /files/<chat_id>/archive). Those URLs are baked into the system prompt today, browser-reachable, and resolve straight to the per-chat outputs/ mount.

This PR introduces a second, parallel path where the same files are also re-uploaded into Open WebUI's native file store and surfaced as /api/v1/files/<id>/content. Could you spell out:

  • What user-visible capability does the OWUI-native file store unlock that the existing PUBLIC_BASE_URL/files/... URLs don't? (Attachment chip in chat UI? RAG indexing? Re-attach across chats?)
  • Why duplicate every output instead of just letting OWUI proxy/fetch from the existing endpoint?
  • When both URLs are present in a tool result, which one should the model prefer, and why? The current prompt guidance is ambiguous.

A short "before/after" example in the PR description (what the model emits today vs. with sync enabled, and what the user sees in OWUI) would go a long way.

2. Bugs / regressions

  • .env.example regression — the diff removes the MCP_TOKENS_URL / MCP_TOKENS_API_KEY documentation block and replaces it with the new OWUI section. Those vars still exist in code; their docs should not disappear. Please add the new section, don't overwrite an unrelated one.
  • Sync runs on every tool call. _sync_outputs_if_configured is wired into bash_tool, str_replace, create_file and sub_agent. Each call does a recursive rglob("*") over outputs/ and MD5s every file unconditionally before consulting the manifest. For a chat with many or large outputs this adds noticeable latency to every bash invocation. At minimum: stat-based pre-check (mtime + size), MD5 only on suspected change. Better: trigger sync explicitly (a dedicated tool or end-of-turn hook) instead of after every bash.
  • format_sync_summary pollutes every tool result. The function appends a Synced to Open WebUI: block whenever the result list is non-empty — but the result list also includes manifest cache hits, not just newly uploaded files. So once a file is synced, every subsequent bash_tool call drags the same Synced to Open WebUI: foo.txt -> /api/v1/files/file-1/content line into its output forever. Only newly-uploaded-this-call entries should be reported.
  • Manifest key collisions. manifest[file_path.name] = ... while files are walked with rglob("*")outputs/a/foo.txt and outputs/b/foo.txt collide. Either flatten the search to iterdir() or key the manifest by the relative path.
  • Dead chat_id parameter. sync_outputs_to_owui(chat_id, ...) does del chat_id on the first line. Either use it (e.g. namespace the manifest, or include in audit logs) or drop the parameter.
  • Fragile system prompt injection. system_prompt.py calls .replace(" - Action: Copy completed files here and share as HTTP links", ...). Any reformat of the prompt template silently disables the OWUI hint with no test failure. Please make this a real template placeholder ({owui_hint} or similar) like the other variables in this file.
  • cgi module in tests. tests/orchestrator/test_owui_sync.py imports cgi, which was removed in Python 3.13. Use email.parser / multipart / werkzeug.formparser, or do the multipart parse manually — the suite will fail outright on a current Python.
  • Inconsistent guidance wording. mcp_tools.sub_agent injects one sentence; system_prompt.py injects a different sentence. Pick one canonical phrasing and reuse it.
  • No filter for in-progress files. Hidden files are skipped, but .crdownload / .part / .tmp / editor swap files will be uploaded mid-write. Add an extension/suffix denylist or a "file is older than N seconds since last mtime" check.
  • Manifest write only at end of loop. If the function is interrupted partway through a multi-file sync, all upload progress is lost and everything re-uploads on the next call. Write incrementally (or use atomic temp+rename per upload).

3. Scope

The compose changes (HOST_COMPUTER_USE_DATA_DIR, MCP_BIND_HOST, cron DATA_DIR) are unrelated to OWUI sync. They look reasonable on their own but please split them into a separate PR — they touch the deploy surface for every existing user and deserve to be reviewed independently.

4. Evidence I'd like to see in the PR before merging

The current Test Plan is just a list of commands without output. Please attach:

  • Full stdout of python -m pytest tests/orchestrator/test_owui_sync.py tests/orchestrator/test_mcp_tools.py -v (the actual log, not a checkbox).
  • The same test run on Python 3.13 (because of the cgi issue above).
  • Screenshot of an Open WebUI chat where a synced output file appears as a native OWUI file (file chip / attachment / wherever it actually shows up — that's the point of the feature).
  • Screenshot or transcript of a tool result showing the appended Synced to Open WebUI: ... block and the model successfully linking to /api/v1/files/<id>/content in its final answer.
  • A quick perf number: time of bash_tool with OWUI_INTERNAL_URL unset vs. set, with e.g. 50 small files in outputs/ already synced (i.e., manifest cache-hit path). This is the realistic steady-state cost.

Once the rationale is documented and the bugs above are addressed I'm happy to do a proper line-level review. Thanks!


Generated by Claude Code

Copy link
Copy Markdown
Owner

Yambr commented May 2, 2026

Following up on §1 of my previous comment with a clearer position on direction.

We can't accept anything that breaks compatibility or inverts the dependency toward Open WebUI, even when the underlying idea is reasonable. This project is positioned as model- and frontend-agnostic ("pluggable into any model"); the orchestrator must not know about OWUI's API, hold an OWUI API key, or act as an OWUI client. Two concrete consequences of the current design that we won't ship as-is:

  • Reverse coupling. Today OWUI calls the orchestrator. This PR makes the orchestrator call OWUI. That makes any non-OWUI frontend a second-class citizen and bakes OWUI specifics into the core.
  • Wrong user context. A single OWUI_API_KEY in the orchestrator means every uploaded file is attributed to whichever OWUI user owns that key (typically an admin), not the end-user of the chat. In any multi-user setup that's broken behaviour.

If you still want this feature, the right shape is to mirror the existing OWUI → workspace sync, in reverse, on the OWUI side. We already do exactly this pattern for the inbound direction:

  • Sync helper: openwebui/tools/computer_use_tools.py:760-828 (_sync_uploaded_files)
  • Call-site / lazy trigger: openwebui/tools/computer_use_tools.py:515-528 (_sync_files_if_needed)

Key properties of that pattern that the reverse version should preserve:

  1. Lives in openwebui/, not in the orchestrator. All OWUI-API knowledge stays on the OWUI side, where it already has the end-user's auth context.
  2. Manifest-driven, server-hosted manifest. The OWUI side fetches a manifest from the orchestrator (e.g. GET /api/outputs/{chat_id}/manifest returning {filename: md5}) and only fetches files whose md5 changed.
  3. Lazy, not per-tool. _sync_files_if_needed only fires when a command actually references the uploads path. The reverse should similarly run on a clear trigger (e.g. outlet at end of turn, or when the assistant message references an output URL) — not on every bash_tool call.
  4. Uses the existing public endpoints. Files are pulled from the orchestrator's existing PUBLIC_BASE_URL/files/{chat_id}/{filename}; no new orchestrator → OWUI codepath, no new OWUI API key in the orchestrator.

Concretely, what we'd accept:

  • A new GET /api/outputs/{chat_id}/manifest on the orchestrator returning {filename: md5} (read-only; no OWUI knowledge).
  • An outlet/post-tool hook in openwebui/tools/computer_use_tools.py (or a small filter) that diffs that manifest against a local one, downloads new files from the existing /files/{chat_id}/... endpoint, and uploads them into OWUI using the caller's OWUI auth — not a server-wide key.
  • Zero new OWUI-specific imports, env vars, or HTTP calls in computer-use-server/.

The compose-side changes (HOST_COMPUTER_USE_DATA_DIR, MCP_BIND_HOST, cron DATA_DIR) are independent and welcome — please split them into their own PR and we can take a look at that one separately.

Happy to review v2 in this shape. The bug list from my previous comment still applies to whatever survives the reshape (especially the cgi-on-3.13 issue and the format_sync_summary cache-hit pollution — those reproduce regardless of where the code lives).


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants