Skip to content

merge: sync upstream main and web-studio file manager into new-frontend #5

Closed
feichuans wants to merge 75 commits into
new-frontendfrom
web
Closed

merge: sync upstream main and web-studio file manager into new-frontend #5
feichuans wants to merge 75 commits into
new-frontendfrom
web

Conversation

@feichuans

Copy link
Copy Markdown
Collaborator

baojun-zhang and others added 30 commits April 5, 2026 13:06
…efs (volcengine#1211)

* fix: PID lock recycle, recall threshold bypass, orphaned compressor refs

1. process_lock: verify PID is actually OpenViking on Linux by checking
   /proc/{pid}/cmdline. Prevents false DataDirectoryLocked when PIDs are
   recycled to unrelated processes after crash (Fixes volcengine#1088).

2. memory-ranking: add scoreThreshold param to pickMemoriesForInjection
   and filter non-leaf items below threshold. Previously low-scoring
   memories bypassed recallScoreThreshold when supplementing leaves
   (Fixes volcengine#1106).

3. compressor: catch FileNotFoundError separately in _merge_into_existing,
   clean up orphaned vector records so they are not retried indefinitely
   (Fixes volcengine#1048).

* style: ruff format process_lock.py

---------

Co-authored-by: JasonOA888 <JasonOA888@users.noreply.github.com>
…olcengine#769) (volcengine#792)

Repeated memory writes enqueue the same parent directory for .overview/.abstract
regeneration, causing redundant VLM work. Skip duplicate enqueues for the same
(account, user, agent, uri) within 45s; resource/session semantic paths unchanged.

Fixes volcengine#769

Made-with: Cursor
…lcengine#1228)

Add README link for Claude Code Memory Plugin example in all language
variants (EN, CN, JA) and add Chinese documentation for the plugin.
…lcengine#1226)

Move redo recovery to a background task so the server starts without
waiting for potentially slow VLM calls. Add 60s timeout to
extract_long_term_memories to prevent indefinite hangs on individual
redo tasks. Clean up the redo task on stop().

Fixes volcengine#1222

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Replace to_string_lossy() with to_str() to preserve valid UTF-8
filenames (Chinese, Japanese, special characters) instead of
silently corrupting them. Non-UTF-8 paths now return a clear
Error::InvalidPath instead of garbled output.

Fixes volcengine#1018

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
…es (volcengine#1223)

Add tests covering the fix in PR volcengine#1163 where decrypt() raised
'Ciphertext too short' on plaintext files shorter than 4 bytes.

Tests added:
- test_decrypt_empty_plaintext: raw b'' returns b''
- test_decrypt_short_plaintext_less_than_4_bytes: b'X', b'AB', b'ABC' return as-is
- test_decrypt_magic_prefix_without_full_header: b'OVE1' raises CorruptedCiphertextError

Co-authored-by: yc111233 <yc111233@users.noreply.github.com>
fix: request wait telemetry id

fix: register request wait before enqueue

add log
* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* reorg: rewrite agfs with rust, and named with ragfs, keep License

* fix: grep level limit

* fix: grep root

* fix: import error

* fix: rust code optimazation

* fix: CI error

* fix: CI go mod cache

* fix: grep level limit

* fix: CI

---------

Co-authored-by: openviking <openviking@example.com>
… private-network SSRF (volcengine#1133)

* Harden HTTP resource ingestion against private-network SSRF

* chore(ci): retrigger checks

* style: fix resource service import order
…nt infinite hang (volcengine#1235)

When Phase 2 of a previous archive crashes after writing messages but
before writing .done or .failed.json, the next archive's memory
extraction enters an infinite polling loop with no exit condition.

Add a 5-minute deadline. On timeout, return False (treated as failure)
so the caller writes .failed.json and the session is no longer stuck.

Co-authored-by: yc111233 <yc111233@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ine#1243)

* fix(bot): respect OPENVIKING_CONFIG_FILE even when file doesn't exist

Previously, bot's config path resolution would fallback to
~/.openviking/ov.conf when OPENVIKING_CONFIG_FILE was set but the
file didn't exist. This was inconsistent with server's behavior,
which treats a missing env-specified config as an error.

In container deployments with OPENVIKING_CONFIG_FILE=/app/ov.conf,
this caused bot to potentially write auto-generated config to
/root/.openviking/ov.conf instead of the intended /app/ov.conf,
leading to config file path mismatches.

Now bot respects the environment variable unconditionally, matching
server's behavior and ensuring both components use the same config
path.

Fixes volcengine#1242

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(embedder): support dimension truncation for OpenAI-compatible models

Previously, when users configured a dimension (e.g., 1024) for OpenAI-compatible
embedding models that don't support the 'dimensions' API parameter, the system
would fail with "dimensions is currently not supported" error.

Additionally, when dimension was not configured, the config layer would return
a hardcoded fallback of 2048, but the actual model might return a different
dimension (e.g., 1024), causing dimension validation failures.

This fix implements vector truncation in OpenAIDenseEmbedder:
- Removes the 'dimensions' parameter from API calls (not supported by all models)
- If user configures dimension=1024 and model returns 2048, truncates to 1024
- If no dimension is configured, uses model's native dimension without truncation
- Applies truncation to both single and batch embedding operations

This allows users to:
1. Use OpenAI-compatible models with custom dimensions via truncation
2. Control vector dimensions for storage optimization
3. Avoid dimension mismatch errors between config and actual embeddings

Fixes volcengine#1238

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: volcengine#1232

---------

Co-authored-by: openviking <openviking@example.com>
Co-authored-by: Claude <noreply@anthropic.com>
Include viking://agent/skills in the autoRecall search alongside
user memories and agent memories. Skills stored in OpenViking are
now auto-injected into context when relevant to the query.

Fixes volcengine#1089

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
- Reduce OS matrix from 5 channels to 3 channels
- Use ubuntu-latest, macos-latest, windows-latest instead of specific versions
- Remove redundant ubuntu-arm and intel macos variants
…olcengine#1284)

- Update provider registry to document MiniMax-M2.7 and MiniMax-M2.7-highspeed
  as the recommended models (replacing the outdated M2.1 example comment)
- Add explicit note that MiniMax does not support system messages (they are
  merged into the first user message automatically)
- Update README to advertise MiniMax-M2.7 and MiniMax-M2.7-highspeed as the
  recommended MiniMax models with configuration instructions
- Add comprehensive unit tests (17 tests) covering:
  - Registry keyword matching for MiniMax-M2.7 and MiniMax-M2.7-highspeed
  - Model prefix resolution (minimax/MiniMax-M2.7)
  - System message merging for both LiteLLMProvider and OpenAICompatibleProvider
  - Edge cases: multiple system messages, no system messages, non-MiniMax models
  - MINIMAX_API_KEY environment variable and international API base URL

Co-authored-by: octo-patch <octo-patch@github.com>
* 增加完整的一键评测脚本

* 增加完整的一键评测脚本
Implements a two-phase benchmark pipeline for evaluating mem0 on the
LoCoMo long-term conversation dataset (10 samples, 1540 non-adversarial QA pairs).

- ingest.py: imports LoCoMo conversation sessions into mem0, using
  sample_id as the userId namespace. All messages use "user" role with
  [SpeakerName]: prefix to preserve two-person dialogue structure.
  Temporal context is added via a [System] prefix on each session.

- eval.py: sends QA questions to an OpenClaw agent backed by the
  openclaw-mem0 plugin. Restarts the gateway per sample to switch the
  active userId, verifies the correct user is loaded before running
  questions, then parallelizes questions within each sample using unique
  session keys. Parses session jsonl to collect accurate per-turn token
  usage. Optionally judges answers with a Volcengine ARK LLM.

- delete_user.py: utility to clear mem0 memories for given user_ids.

- README.md: documents prerequisites, ingest/eval parameters, output
  format, and per-sample run commands.
kaisongli and others added 24 commits April 9, 2026 15:05
…it (volcengine#1327)

- Only run full OS matrix (ubuntu-24.04, macos-14, windows-latest) on main branch pushes
- PR branches use ubuntu-24.04 only to reduce runner wait time
- Add max-parallel: 2 to limit concurrent job execution
Co-authored-by: openviking <openviking@example.com>
Add solution for health check tools report http 404 error
Track semantic and embedding re-enqueues as first-class queue metrics so
observer output, wait_processed payloads, and telemetry summaries make
retry loops visible before they escalate into hard errors.
* fix(resources): improve handling of resource imports and naming

- Add source_name to file upload requests for preserving original filenames
- Handle single-directory zip files by using their root directory directly
- Support viking://resources as parent directory for imports
- Split summarization for resources root imports into individual child items
- Add tests for new resource import behaviors

* style(tests): format test files with consistent line breaks

Improve readability by applying consistent line breaks in test file patches and removing trailing whitespace
…onality

- Added FileTree component for displaying directory structure.
- Introduced VikingFileManager component to manage file operations and navigation.
- Created hooks for fetching file system data and handling file previews.
- Implemented API functions for interacting with the file system.
- Normalized file system entries and URIs for consistent handling.
- Updated routing to support file manager navigation with URI segments.
- Enhanced UI components for better user experience in file management.
…e#1342)

Co-authored-by: haosenwang1018 <haosenwang1018@users.noreply.github.com>
)

Ensure the add-message API materializes a missing session before loading it so
plugins can append the first turn without an explicit create_session call.
* fix: make api_test more robust for CI environments

- Add @pytest.hookimpl(optionalhook=True) for pytest-html hooks to fix compatibility issues
- test_fs_read: skip test when AGFS service is not available
- test_get_overview: skip test when overview file does not exist

These changes ensure tests pass gracefully on CI servers where AGFS service
may not be available or files may not exist.

* ci: reduce max-parallel to 1 for better resource availability

Reduce max-parallel from 2 to 1 to avoid waiting for multiple runners
when GitHub-hosted runners are limited.
index_resource() always passed the default context_type="resource" to
vectorize_directory_meta() and vectorize_file(), even when indexing
memory directories (URIs containing /memories/).  This caused all
records created via the /api/v1/content/reindex endpoint to be tagged
as "resource" instead of "memory", breaking stats aggregation
(which filters on context_type="memory") and search scoping.

Use the existing get_context_type_for_uri() helper to derive the
correct context_type from the URI and pass it through to both
vectorize_directory_meta() and vectorize_file().

Co-authored-by: yc111233 <yc111233@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: fix docker deployment

* reorg: remove third_party/agfs

* feat(s3fs): add disable_batch_delete option for OSS compatibility

Port of PR volcengine#1333 from Go version to Rust:

- Add disable_batch_delete config option to S3Client
- When enabled, use sequential single-object deletes instead of DeleteObjects
- This is for S3-compatible services like Alibaba Cloud OSS that require
  Content-MD5 for DeleteObjects but AWS SDK v2 does not send it by default
- Add documentation and config example for OSS

* fix(s3fs): pass disable_batch_delete config from Python to Rust

Add disable_batch_delete to the s3_plugin_config dict in _generate_plugin_config
so that the Python config can properly control the Rust S3FS plugin's behavior.

* reorg: remove third_party/agfs

* reorg: remove third_party/agfs

* change some docs

* change some docs

---------

Co-authored-by: openviking <openviking@example.com>
* fix(memory): handle string response from VLM when tools disabled

- Fix AttributeError when VLM returns string instead of VLMResponse
- Fix tuple creation bug with trailing comma causing double-nested tools array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* update

* chore: replace LGBTQ example with book club in entities.yaml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(memory): disable tools on extended iteration to prevent infinite loop

When max_iterations is extended due to tool calls, disable tools for the
additional iteration to ensure the extract loop terminates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(memory): handle out-of-bounds range from LLM extraction

Clamp range values to valid message indices instead of skipping,
to handle cases where LLM extracts incorrect ranges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…l style

- Progressive panel layout: 1-panel (root), 2-panel (folder/file view)
- Move toolbar buttons into tree sidebar, breadcrumb into file list header
- Replace table with flat list layout, size/date aligned to right
- Remove page shell title, section headers, and preview bottom path bar
- Folder icons: filled dark gray, selection highlight changed to gray
- Sidebar: narrowed to 11rem, border replaced with shadow, rail removed
- Full-bleed layout fused with system sidebar, no boundary gap
- Clean up unused imports and header breadcrumbs from root layout
- Resizable file tree panel with drag handle (160-600px range)
- Layout: grid-cols → flex for proper resize support
- Fix tree expand height: replace max-h-[1000px] with grid-rows animation
- Align tree toolbar and breadcrumb bar heights to h-10
- Clicking directory name no longer auto-expands (expand only via chevron)
- Add max-w-5xl to preview content area
- Clean up FileTree: remove unused FileIcon, type field, isFolder checks
- Add alphabetical sort to tree children
- Remove unused Dialog preview code
@github-actions

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request replaces the Go-based AGFS with a Rust implementation (RAGFS), introducing native Python bindings and a new server architecture. It also enhances the vikingbot agent with improved memory retrieval, session management via a new /new command, and refined tool filtering. The LoCoMo benchmark suite and CLI tool received significant updates, including new search constraints and export capabilities. Feedback highlights several critical areas for improvement: hardcoded absolute paths in shell scripts, inefficient memory handling when reading large files, and dangerous recursive deletion logic for S3 root directories. Additionally, the reviewer noted performance bottlenecks caused by blocking I/O in async functions and sequential network requests, as well as potential race conditions in non-atomic database writes and insecure temporary file usage in the build process.

Comment on lines +56 to +63
echo ""
echo "Step 0b: Deleting data directory /Users/bytedance/.openviking/data..."
if [ -d "/Users/bytedance/.openviking/data" ]; then
rm -rf /Users/bytedance/.openviking/data
echo " ✓ Deleted /Users/bytedance/.openviking/data"
else
echo " ✓ Data directory does not exist"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The script contains a hardcoded absolute path /Users/bytedance/.openviking/data. This will cause the script to fail or delete incorrect data on any machine where the user is not bytedance. Use the $HOME environment variable instead.

Suggested change
echo ""
echo "Step 0b: Deleting data directory /Users/bytedance/.openviking/data..."
if [ -d "/Users/bytedance/.openviking/data" ]; then
rm -rf /Users/bytedance/.openviking/data
echo " ✓ Deleted /Users/bytedance/.openviking/data"
else
echo " ✓ Data directory does not exist"
fi
echo ""
echo "Step 0b: Deleting data directory $HOME/.openviking/data..."
if [ -d "$HOME/.openviking/data" ]; then
rm -rf "$HOME/.openviking/data"
echo " ✓ Deleted $HOME/.openviking/data"
else
echo " ✓ Data directory does not exist"
fi

Comment on lines +164 to +174
let data = fs::read(&local_path)
.map_err(|e| Error::plugin(format!("failed to read file: {}", e)))?;

// Apply offset and size
let file_size = data.len() as u64;
let start = offset.min(file_size) as usize;
let end = if size == 0 {
data.len()
} else {
(offset + size).min(file_size) as usize
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The current implementation reads the entire file into memory using fs::read before applying the offset and size constraints. This is extremely inefficient for large files and can lead to Out-Of-Memory (OOM) crashes. Additionally, fs::read is a blocking operation. It is recommended to use tokio::fs::File, seek to the offset, and read only the requested number of bytes.

Comment on lines +203 to +206
// Delete everything under prefix
self.client.delete_directory("").await?;
self.dir_cache.invalidate_prefix("/").await;
self.stat_cache.invalidate_prefix("/").await;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Executing remove_all on the root directory (/) will recursively delete all objects in the S3 bucket (or under the configured prefix). This is extremely dangerous and should be restricted or require an explicit confirmation/force flag to prevent accidental data loss.

Comment thread Makefile
cd crates/ragfs-python && $$MATURIN_CMD build --release --out "$$TMPDIR" 2>&1; \
cd ../..; \
mkdir -p openviking/lib; \
echo "import zipfile, glob, shutil, os, sys" > /tmp/extract_ragfs.py; \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Writing to a fixed filename /tmp/extract_ragfs.py is insecure and can lead to race conditions if multiple builds run on the same machine. Since a unique temporary directory TMPDIR is already created, the script should be placed inside it. Note that you will also need to update the execution and removal lines (120-121) to use the new path.

		echo "import zipfile, glob, shutil, os, sys" > "$$TMPDIR/extract_ragfs.py"; \

})?;
zip.start_file(name_str, options)?;
let mut file = File::open(path)?;
std::io::copy(&mut file, &mut zip)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The std::io::copy operation is blocking and is being called within an async function. This can block the async runtime's worker threads. Consider using tokio::io::copy with a tokio::fs::File or wrapping the entire synchronous operation in tokio::task::spawn_blocking.

}

// Write file
std::fs::write(&final_path, bytes)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using std::fs::write in an async function blocks the executor thread. This can lead to performance degradation and thread starvation. Use tokio::fs::write instead.

Suggested change
std::fs::write(&final_path, bytes)?;
tokio::fs::write(&final_path, bytes).await?;

Comment on lines +322 to +348
let backend = self.backend.read().await;

let exists = backend.path_exists(&normalized)?;

if exists {
// Check if it's a directory
if backend.is_directory(&normalized)? {
return Err(Error::IsADirectory(normalized));
}

// Update existing file
backend.update_file(&normalized, data)?;
} else {
// Create new file
if !matches!(flags, WriteFlag::Create) {
return Err(Error::not_found(&normalized));
}

// Check parent exists
let parent = backend.parent_path(&normalized);
if parent != "/" {
if !backend.is_directory(&parent)? {
return Err(Error::not_found(&parent));
}
}

backend.create_file(&normalized, 0o644, data)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The write operation uses a check-then-act pattern (path_exists followed by update_file or create_file) which is not atomic and prone to race conditions. It is better to use an UPSERT pattern (e.g., INSERT OR REPLACE or ON CONFLICT) in the database backend to ensure atomicity.

Comment on lines +53 to +109
for idx, memory in enumerate(filtered_memories, start=1):
uri = getattr(memory, "uri", "")
abstract = getattr(memory, "abstract", "")
score = getattr(memory, "score", 0.0)

# First, try to build full memory with content
try:
content = await client.read_content(uri, level="read")
except Exception:
content = ""

if content:
# Try full version first (no abstract when content is present)
memory_str = (
f'<memory index="{idx}" type="full">\n'
f" <uri>{uri}</uri>\n"
f" <score>{score}</score>\n"
f" <content>{content}</content>\n"
f"</memory>"
)
else:
# No content available, use link-only version
memory_str = (
f'<memory index="{idx}" type="link">\n'
f" <uri>{uri}</uri>\n"
f" <score>{score}</score>\n"
f"</memory>"
)
return "\n".join(user_memories)
return ""

# Check if adding this memory would exceed the limit
memory_chars = len(memory_str)
if user_memories:
memory_chars += 1

if total_chars + memory_chars <= max_chars:
user_memories.append(memory_str)
total_chars += memory_chars
else:
# If full version is too big, try link-only version
link_only_str = (
f'<memory index="{idx}" type="link">\n'
f" <uri>{uri}</uri>\n"
f" <score>{score}</score>\n"
f"</memory>"
)
link_chars = len(link_only_str)
if user_memories:
link_chars += 1

if total_chars + link_chars <= max_chars:
user_memories.append(link_only_str)
total_chars += link_chars
else:
# Even link-only is too big, skip this memory
continue

return "\n".join(user_memories)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The _parse_viking_memory function performs sequential await client.read_content calls for each memory entry. With the increased limit of 30 memories, this can significantly increase latency. It is recommended to fetch the contents in parallel using asyncio.gather before iterating to build the result string.

user_results = []
if messages_by_sender:
# 限制最大并发数为 5
semaphore = asyncio.Semaphore(5)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The semaphore is instantiated inside the execute method, meaning it only limits concurrency within a single hook execution. If multiple requests trigger this hook simultaneously, the total number of concurrent commits to the server will not be restricted. Consider moving the semaphore to the class level or a shared configuration to effectively manage global concurrency.

@feichuans feichuans requested review from Ferry-200 and Jye10032 April 11, 2026 09:04
@feichuans feichuans closed this Apr 11, 2026
@feichuans feichuans deleted the web branch April 12, 2026 05:55
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.