chore: remove useless docs, mv some code to better places (#5244)
@@ -231,7 +231,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
OPENVIKING_VERSION=$(
|
||||
uv run --frozen python -c "from build_support.versioning import resolve_openviking_version; print(resolve_openviking_version())"
|
||||
uv run --frozen python -c "from scripts.build_support.versioning import resolve_openviking_version; print(resolve_openviking_version())"
|
||||
)
|
||||
echo "OPENVIKING_VERSION=$OPENVIKING_VERSION" >> "$GITHUB_ENV"
|
||||
echo "Resolved OpenViking version: $OPENVIKING_VERSION"
|
||||
@@ -465,7 +465,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
OPENVIKING_VERSION=$(
|
||||
uv run --frozen python -c "from build_support.versioning import resolve_openviking_version; print(resolve_openviking_version())"
|
||||
uv run --frozen python -c "from scripts.build_support.versioning import resolve_openviking_version; print(resolve_openviking_version())"
|
||||
)
|
||||
echo "OPENVIKING_VERSION=$OPENVIKING_VERSION" >> "$GITHUB_ENV"
|
||||
echo "Resolved OpenViking version: $OPENVIKING_VERSION"
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
else
|
||||
python -m pip install "setuptools-scm>=8.0"
|
||||
version="$(
|
||||
python -c "from build_support.versioning import resolve_openviking_version; print(resolve_openviking_version())"
|
||||
python -c "from scripts.build_support.versioning import resolve_openviking_version; print(resolve_openviking_version())"
|
||||
)"
|
||||
fi
|
||||
if [ -z "${version}" ] || [ "${version}" = "0.0.0" ]; then
|
||||
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
echo "cuvs_changed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
LANGCHAIN_PATTERN="integrations/langchain/|openviking/integrations/langchain/|tests/(unit/test_langchain|integration/langchain_langgraph/)|examples/langchain-langgraph/|pyproject\.toml|uv\.lock|\.github/workflows/(pr|python-langchain-release)\.yml"
|
||||
LANGCHAIN_PATTERN="examples/langchain/|openviking/integrations/langchain/|tests/(unit/test_langchain|integration/langchain_langgraph/)|examples/langchain-langgraph/|pyproject\.toml|uv\.lock|\.github/workflows/(pr|python-langchain-release)\.yml"
|
||||
LANGCHAIN_CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }} HEAD | grep -E "$LANGCHAIN_PATTERN" || true)
|
||||
|
||||
if [ -n "$LANGCHAIN_CHANGED_FILES" ]; then
|
||||
@@ -291,19 +291,19 @@ jobs:
|
||||
- name: Install standalone integration
|
||||
run: |
|
||||
uv pip install --system -e sdk/python
|
||||
uv pip install --system -e "integrations/langchain[langgraph,test,dev]"
|
||||
uv pip install --system -e "examples/langchain[langgraph,test,dev]"
|
||||
|
||||
- name: Check package formatting and lint
|
||||
run: |
|
||||
ruff format --check \
|
||||
integrations/langchain/src \
|
||||
integrations/langchain/tests \
|
||||
examples/langchain/src \
|
||||
examples/langchain/tests \
|
||||
openviking/integrations/langchain \
|
||||
tests/integration/langchain_langgraph \
|
||||
tests/unit/test_langchain*.py
|
||||
ruff check \
|
||||
integrations/langchain/src \
|
||||
integrations/langchain/tests \
|
||||
examples/langchain/src \
|
||||
examples/langchain/tests \
|
||||
openviking/integrations/langchain \
|
||||
tests/integration/langchain_langgraph \
|
||||
tests/unit/test_langchain*.py
|
||||
@@ -311,8 +311,8 @@ jobs:
|
||||
- name: Type-check package
|
||||
run: >-
|
||||
mypy
|
||||
--config-file integrations/langchain/pyproject.toml
|
||||
integrations/langchain/src/langchain_openviking
|
||||
--config-file examples/langchain/pyproject.toml
|
||||
examples/langchain/src/langchain_openviking
|
||||
|
||||
- name: Run LangChain and LangGraph tests
|
||||
run: |
|
||||
@@ -322,13 +322,13 @@ jobs:
|
||||
|
||||
- name: Build and inspect distributions
|
||||
run: |
|
||||
python -m build integrations/langchain
|
||||
python -m twine check integrations/langchain/dist/*
|
||||
python -m build examples/langchain
|
||||
python -m twine check examples/langchain/dist/*
|
||||
|
||||
- name: Verify the installed wheel
|
||||
if: matrix.python-version == '3.12'
|
||||
run: |
|
||||
WHEEL_PATH="${GITHUB_WORKSPACE}/$(find integrations/langchain/dist -name '*.whl' -print -quit)"
|
||||
WHEEL_PATH="${GITHUB_WORKSPACE}/$(find examples/langchain/dist -name '*.whl' -print -quit)"
|
||||
uv venv .langchain-wheel-venv --python "${{ matrix.python-version }}"
|
||||
uv pip install \
|
||||
--python .langchain-wheel-venv/bin/python \
|
||||
@@ -336,10 +336,10 @@ jobs:
|
||||
"${WHEEL_PATH}"
|
||||
cd /tmp
|
||||
"${GITHUB_WORKSPACE}/.langchain-wheel-venv/bin/python" \
|
||||
"${GITHUB_WORKSPACE}/integrations/langchain/tests/installed_base_smoke.py"
|
||||
"${GITHUB_WORKSPACE}/examples/langchain/tests/installed_base_smoke.py"
|
||||
uv pip install \
|
||||
--python "${GITHUB_WORKSPACE}/.langchain-wheel-venv/bin/python" \
|
||||
"${WHEEL_PATH}[langgraph]"
|
||||
"${GITHUB_WORKSPACE}/.langchain-wheel-venv/bin/python" \
|
||||
"${GITHUB_WORKSPACE}/integrations/langchain/tests/installed_smoke.py"
|
||||
"${GITHUB_WORKSPACE}/examples/langchain/tests/installed_smoke.py"
|
||||
uv pip check --python "${GITHUB_WORKSPACE}/.langchain-wheel-venv/bin/python"
|
||||
|
||||
@@ -50,12 +50,12 @@ jobs:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install build dependencies
|
||||
working-directory: integrations/langchain
|
||||
working-directory: examples/langchain
|
||||
run: python -m pip install build setuptools setuptools-scm twine wheel
|
||||
|
||||
- name: Resolve package version
|
||||
id: version
|
||||
working-directory: integrations/langchain
|
||||
working-directory: examples/langchain
|
||||
run: |
|
||||
PACKAGE_VERSION=$(python -m setuptools_scm)
|
||||
echo "package_version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
|
||||
@@ -74,18 +74,18 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Build distributions
|
||||
working-directory: integrations/langchain
|
||||
working-directory: examples/langchain
|
||||
run: python -m build
|
||||
|
||||
- name: Validate distributions
|
||||
working-directory: integrations/langchain
|
||||
working-directory: examples/langchain
|
||||
run: python -m twine check dist/*
|
||||
|
||||
- name: Upload distributions
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: langchain-openviking-distributions
|
||||
path: integrations/langchain/dist/*
|
||||
path: examples/langchain/dist/*
|
||||
|
||||
publish-testpypi:
|
||||
name: Publish to TestPyPI
|
||||
|
||||
@@ -57,7 +57,7 @@ glob = [
|
||||
# Worktree scratch spaces
|
||||
'.worktrees/**',
|
||||
# Build support (C++ profiles, not core logic)
|
||||
'build_support/**',
|
||||
'scripts/build_support/**',
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -89,7 +89,7 @@ only the contacts relevant to the change.
|
||||
| Storage | RAGFS, PathLock, QueueFS, and encryption | `openviking/storage`, `openviking/pyagfs`, `openviking/crypto`, `crates/ragfs*` | `@baojun-zhang` |
|
||||
| Integration | Agent plugins and MCP | `agent-plugins`, memory plugin examples, server MCP | `@t0saki`, `@ZaynJarvis` |
|
||||
| Integration | VikingBot and agent compilation | `bot`, `ov compile` | `@yeshion23333`, `@fujiajie666` |
|
||||
| Client | SDKs, CLI, and LangChain | `sdk`, `crates/ov_cli`, `integrations/langchain` | `@zhoujh01`, `@t0saki`, `@ehz0ah` |
|
||||
| Client | SDKs, CLI, and LangChain | `sdk`, `crates/ov_cli`, `examples/langchain` | `@zhoujh01`, `@t0saki`, `@ehz0ah` |
|
||||
| Product | Web Studio | `web-studio` | `@yufeng201`, `@ZaynJarvis` |
|
||||
| Project | Documentation, CI, and plugin releases | `docs`, `.github/workflows` | `@yufeng201`, `@ZaynJarvis` |
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ OpenViking 重视聚焦且经过充分理解的改动。无论是否使用 AI
|
||||
| Storage | RAGFS、PathLock、QueueFS 与加密 | `openviking/storage`、`openviking/pyagfs`、`openviking/crypto`、`crates/ragfs*` | `@baojun-zhang` |
|
||||
| Integration | Agent Plugin 与 MCP | `agent-plugins`、记忆插件示例、Server MCP | `@t0saki`、`@ZaynJarvis` |
|
||||
| Integration | VikingBot 与 Agent 编译 | `bot`、`ov compile` | `@yeshion23333`、`@fujiajie666` |
|
||||
| Client | SDK、CLI 与 LangChain | `sdk`、`crates/ov_cli`、`integrations/langchain` | `@zhoujh01`、`@t0saki`、`@ehz0ah` |
|
||||
| Client | SDK、CLI 与 LangChain | `sdk`、`crates/ov_cli`、`examples/langchain` | `@zhoujh01`、`@t0saki`、`@ehz0ah` |
|
||||
| Product | Web Studio | `web-studio` | `@yufeng201`、`@ZaynJarvis` |
|
||||
| Project | 文档、CI 与 Plugin 发布 | `docs`、`.github/workflows` | `@yufeng201`、`@ZaynJarvis` |
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ OpenVikingでは、焦点が絞られ、十分に理解された変更を重視
|
||||
| Storage | RAGFS、PathLock、QueueFS、暗号化 | `openviking/storage`、`openviking/pyagfs`、`openviking/crypto`、`crates/ragfs*` | `@baojun-zhang` |
|
||||
| Integration | Agent PluginとMCP | `agent-plugins`、メモリPluginの例、Server MCP | `@t0saki`、`@ZaynJarvis` |
|
||||
| Integration | VikingBotとAgentコンパイル | `bot`、`ov compile` | `@yeshion23333`、`@fujiajie666` |
|
||||
| Client | SDK、CLI、LangChain | `sdk`、`crates/ov_cli`、`integrations/langchain` | `@zhoujh01`、`@t0saki`、`@ehz0ah` |
|
||||
| Client | SDK、CLI、LangChain | `sdk`、`crates/ov_cli`、`examples/langchain` | `@zhoujh01`、`@t0saki`、`@ehz0ah` |
|
||||
| Product | Web Studio | `web-studio` | `@yufeng201`、`@ZaynJarvis` |
|
||||
| Project | ドキュメント、CI、Pluginリリース | `docs`、`.github/workflows` | `@yufeng201`、`@ZaynJarvis` |
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ WORKDIR /app
|
||||
# Copy source required for setup.py artifact builds and native extension build.
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY pyproject.toml uv.lock setup.py README.md ./
|
||||
COPY build_support/ build_support/
|
||||
COPY scripts/build_support/ scripts/build_support/
|
||||
COPY bot/ bot/
|
||||
COPY crates/ crates/
|
||||
COPY openviking/ openviking/
|
||||
@@ -113,8 +113,8 @@ COPY --from=py-builder /app/.venv /app/.venv
|
||||
# Fail the image build if VikingBot and the separately released SDK drift apart.
|
||||
RUN /app/.venv/bin/python -I -c "import inspect; from importlib.metadata import version; from openviking_sdk.client import AsyncHTTPClient; signature = inspect.signature(AsyncHTTPClient.get_skill); raise SystemExit(0 if 'include_integrity' in signature.parameters else f\"incompatible openviking-sdk {version('openviking-sdk')}: AsyncHTTPClient.get_skill{signature} lacks include_integrity\")"
|
||||
RUN /app/.venv/bin/python -I -c "from importlib.util import find_spec; from pathlib import Path; spec = find_spec('openviking.web_studio'); locations = list(spec.submodule_search_locations or ()) if spec else []; root = Path('/app/.venv').resolve(); p = (Path(locations[0]).resolve() / 'dist/index.html').resolve() if len(locations) == 1 else None; valid = p is not None and p.is_file() and p.is_relative_to(root); raise SystemExit(0 if valid else f'missing or misplaced Studio bundle: spec_found={spec is not None}, locations={locations!r}, resource={p}')"
|
||||
COPY docker/openviking-entrypoint.sh /usr/local/bin/openviking-entrypoint
|
||||
COPY docker/pending_health_server.py /usr/local/bin/openviking-pending-health
|
||||
COPY deploy/docker/openviking-entrypoint.sh /usr/local/bin/openviking-entrypoint
|
||||
COPY deploy/docker/pending_health_server.py /usr/local/bin/openviking-pending-health
|
||||
RUN mkdir -p /app/.openviking \
|
||||
&& sed -i 's/\r$//' /usr/local/bin/openviking-entrypoint /usr/local/bin/openviking-pending-health \
|
||||
&& chmod +x /usr/local/bin/openviking-entrypoint /usr/local/bin/openviking-pending-health
|
||||
|
||||
@@ -72,7 +72,7 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
"volcengine>=1.0.216"
|
||||
COPY examples/cuvs_smoke.py /opt/openviking-cuvs/cuvs_smoke.py
|
||||
COPY benchmark/cuvs/ /opt/openviking-cuvs-benchmark/
|
||||
COPY docker/cuvs-dev/entrypoint.sh /usr/local/bin/openviking-cuvs-dev
|
||||
COPY deploy/docker/cuvs-dev/entrypoint.sh /usr/local/bin/openviking-cuvs-dev
|
||||
RUN cp -a /opt/openviking-native-engine/. \
|
||||
/opt/openviking/openviking/storage/vectordb/engine/ \
|
||||
&& chmod +x /usr/local/bin/openviking-cuvs-dev \
|
||||
@@ -10,7 +10,7 @@ Build it from the repository root:
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
-f docker/cuvs-dev/Dockerfile \
|
||||
-f deploy/docker/cuvs-dev/Dockerfile \
|
||||
-t openviking-cuvs:dev \
|
||||
.
|
||||
```
|
||||
@@ -22,7 +22,7 @@ driver/runtime policy requires it:
|
||||
docker build \
|
||||
--build-arg CUVS_PACKAGE=cuvs-cu12==26.6.0 \
|
||||
--build-arg 'CUPY_PACKAGE=cupy-cuda12x[ctk]==14.1.1' \
|
||||
-f docker/cuvs-dev/Dockerfile \
|
||||
-f deploy/docker/cuvs-dev/Dockerfile \
|
||||
-t openviking-cuvs:dev-cu12 \
|
||||
.
|
||||
```
|
||||
@@ -64,7 +64,7 @@ For an Enroot/Pyxis environment, export the already-built Docker image once to
|
||||
a shared SquashFS file:
|
||||
|
||||
```bash
|
||||
docker/cuvs-dev/export-sqsh.sh \
|
||||
deploy/docker/cuvs-dev/export-sqsh.sh \
|
||||
openviking-cuvs:dev \
|
||||
/shared/images/openviking-cuvs-dev.sqsh
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 351 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 404 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
@@ -1 +0,0 @@
|
||||
|
||||
@@ -308,11 +308,11 @@ finish an async lifecycle with `recorder.close()`.
|
||||
The repository includes runnable examples that work without model credentials using an in-memory test client:
|
||||
|
||||
```bash
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langchain/rag/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langchain/context-backend/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langchain/message-history/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langgraph/agent/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langgraph/middleware/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langchain/rag/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langchain/context-backend/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langchain/message-history/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langgraph/agent/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langgraph/middleware/quick_app.py
|
||||
```
|
||||
|
||||
For a real OpenViking server and OpenAI-compatible model flow, see the [live LangGraph app](https://github.com/volcengine/OpenViking/blob/main/examples/langchain-langgraph/langgraph/agent/live_app.py).
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
# Shared Temp Upload Timestamp Directory Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Store each shared temporary upload in a timestamp-prefixed directory so cleanup can determine expiry from one root listing without filesystem modification times.
|
||||
|
||||
**Architecture:** Generate shared upload IDs as a fixed-width Unix-millisecond timestamp followed by a UUID. Use that ID as the directory name under `viking://upload`, with `content` written before `meta`. Consumers construct both paths directly from `temp_file_id`; cleanup parses timestamps from first-level directory names and removes expired directories recursively.
|
||||
|
||||
**Tech Stack:** Python async server, VikingFS, pytest, Markdown documentation.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Define directory-layout cleanup behavior
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/server/test_temp_upload_store.py`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Replace the flat-object cleanup test with a test that returns first-level timestamp directories and verifies only expired valid directories are removed recursively without `modTime`.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `uv run --active pytest tests/server/test_temp_upload_store.py -q`
|
||||
|
||||
Expected: FAIL because cleanup still expects `.content` and `.meta` objects.
|
||||
|
||||
**Step 3: Implement minimal cleanup behavior**
|
||||
|
||||
Parse a fixed-width millisecond timestamp from directory names, skip malformed entries, and remove only expired directories.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `uv run --active pytest tests/server/test_temp_upload_store.py -q`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### Task 2: Switch shared upload save and resolve paths
|
||||
|
||||
**Files:**
|
||||
- Modify: `openviking/server/temp_upload_store.py`
|
||||
- Modify: `tests/server/test_api_resources.py`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Assert uploaded shared files are stored in `viking://upload/<timestamp>-<uuid>/content` and `meta`, while the returned `temp_file_id` stays `shared_<upload_id>`.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `uv run --active pytest tests/server/test_api_resources.py -q -k shared_temp_upload`
|
||||
|
||||
Expected: FAIL because the implementation writes flat `.content` and `.meta` objects.
|
||||
|
||||
**Step 3: Implement minimal storage behavior**
|
||||
|
||||
Generate timestamp-prefixed IDs, construct directory child URIs, write content before metadata, and clean up the directory recursively after a partial write failure.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `uv run --active pytest tests/server/test_api_resources.py -q -k shared_temp_upload`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### Task 3: Update user-deletion cleanup
|
||||
|
||||
**Files:**
|
||||
- Modify: `openviking/service/user_deletion.py`
|
||||
- Modify: `tests/server/test_temp_upload_store.py`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Make user deletion list upload directories, read each directory’s `meta`, and remove only directories owned by the deleted user.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `uv run --active pytest tests/server/test_temp_upload_store.py -q`
|
||||
|
||||
Expected: FAIL because deletion still scans root-level `.meta` objects.
|
||||
|
||||
**Step 3: Implement minimal deletion behavior**
|
||||
|
||||
Read `viking://upload/<upload_id>/meta` for each valid upload directory and recursively remove the owned directory.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `uv run --active pytest tests/server/test_temp_upload_store.py -q`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### Task 4: Document and verify the layout
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/en/api/02-resources.md`
|
||||
- Modify: `docs/zh/api/02-resources.md`
|
||||
- Modify: `docs/en/guides/01-configuration.md`
|
||||
- Modify: `docs/zh/guides/01-configuration.md`
|
||||
|
||||
**Step 1: Update documentation**
|
||||
|
||||
Describe the timestamp-prefixed directory layout and timestamp-based one-listing cleanup, without referring to object modification times.
|
||||
|
||||
**Step 2: Run focused verification**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --active pytest tests/test_config_loader.py tests/server/test_temp_upload_store.py -q
|
||||
uv run --active pytest tests/server/test_api_resources.py -q -k shared_temp_upload
|
||||
uv run --active ruff check openviking/server/temp_upload_store.py openviking/service/user_deletion.py tests/server/test_temp_upload_store.py tests/server/test_api_resources.py
|
||||
```
|
||||
|
||||
Expected: all commands pass.
|
||||
@@ -1,114 +0,0 @@
|
||||
# Memory Overview Lock Coverage Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Ensure each streaming memory update's exact-path batch lease covers the `.overview.md` file regenerated for every upserted or deleted memory directory.
|
||||
|
||||
**Architecture:** Extend `_operation_lock_paths` at the point where it still has the logical operation URI set. Derive one sibling `.overview.md` URI per directly mutated memory file, merge those URIs into the existing exact-lock set, then retain the current URI-to-path conversion, deduplication, sorting, lease acquisition, and propagation.
|
||||
|
||||
**Tech Stack:** Python 3.10+, pytest, Ruff, VikingFS/RagFS pathlock APIs.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Use exact locks for `.overview.md`; do not replace file locks with directory tree locks.
|
||||
- Add overview paths only for upsert and delete targets that cause `MemoryUpdater` to regenerate an overview.
|
||||
- Preserve existing replacement and link endpoint lock coverage.
|
||||
- Keep empty-directory recursive deletion behavior outside this focused change.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Cover derived overview files in the streaming update lease
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/session/memory/test_streaming_memory_updater.py:406-445`
|
||||
- Modify: `openviking/session/memory/streaming_memory_updater.py:1840-1864`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `_operation_uri_set(operations: ResolvedOperations | None) -> set[str]` and `_uri_lock_paths(uris: set[str], viking_fs: Any | None, ctx: RequestContext) -> list[str]`.
|
||||
- Produces: `_operation_lock_paths(operations: ResolvedOperations, viking_fs: Any | None, ctx: RequestContext) -> list[str]` with exact paths for directly mutated files and one `.overview.md` per directly affected parent directory.
|
||||
|
||||
- [x] **Step 1: Extend the existing regression expectation**
|
||||
|
||||
Update `test_operation_lock_paths_cover_deletes_replacements_and_link_endpoints` so its literal expected list contains:
|
||||
|
||||
```python
|
||||
assert _operation_lock_paths(operations, fs, _ctx()) == [
|
||||
"/user/u/memories/notes/.overview.md",
|
||||
"/user/u/memories/notes/deleted.md",
|
||||
"/user/u/memories/notes/inherited_neighbor.md",
|
||||
"/user/u/memories/notes/neighbor.md",
|
||||
"/user/u/memories/notes/replacement.md",
|
||||
"/user/u/memories/notes/updated.md",
|
||||
]
|
||||
```
|
||||
|
||||
This catches the production bug where `_operation_lock_paths` omits the sidecar path. Because the fixture contains both an upsert and delete in the same directory, it also proves set-based deduplication produces one overview path while replacement and link endpoint paths remain covered.
|
||||
|
||||
- [x] **Step 2: Run the regression test and verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py::test_operation_lock_paths_cover_deletes_replacements_and_link_endpoints -q
|
||||
```
|
||||
|
||||
Expected: FAIL because the actual list does not contain `/user/u/memories/notes/.overview.md`.
|
||||
|
||||
- [x] **Step 3: Add a distinct-directory regression test**
|
||||
|
||||
Add a test that builds one upsert under `notes/` and one delete under `events/2023/02/01/`, then asserts the literal result contains both memory paths and exactly these two derived paths:
|
||||
|
||||
```python
|
||||
[
|
||||
"/user/u/memories/events/2023/02/01/.overview.md",
|
||||
"/user/u/memories/events/2023/02/01/deleted.md",
|
||||
"/user/u/memories/notes/.overview.md",
|
||||
"/user/u/memories/notes/updated.md",
|
||||
]
|
||||
```
|
||||
|
||||
- [x] **Step 4: Implement the minimal URI derivation**
|
||||
|
||||
In `_operation_lock_paths`, retain the directly mutated URI set separately and add each valid parent overview URI before collecting replacements and link endpoints:
|
||||
|
||||
```python
|
||||
operation_uris = _operation_uri_set(operations)
|
||||
uris = set(operation_uris)
|
||||
for uri in operation_uris:
|
||||
normalized_uri = str(uri).rstrip("/")
|
||||
directory, separator, _ = normalized_uri.rpartition("/")
|
||||
if separator and directory:
|
||||
uris.add(f"{directory}/.overview.md")
|
||||
```
|
||||
|
||||
Do not derive overview paths from `resolved_links`, `delete_replacements`, or inherited link endpoints because those URI categories do not directly drive the overview-generation loop in `MemoryUpdater.apply_operations`.
|
||||
|
||||
- [x] **Step 5: Run focused tests and verify GREEN**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py::test_operation_lock_paths_cover_deletes_replacements_and_link_endpoints tests/session/memory/test_streaming_memory_updater.py::test_operation_lock_paths_cover_each_affected_overview_directory -q
|
||||
```
|
||||
|
||||
Expected: 2 passed.
|
||||
|
||||
- [x] **Step 6: Run relevant regression suites and static checks**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py tests/session/memory/test_memory_updater.py -q
|
||||
uv run ruff check openviking/session/memory/streaming_memory_updater.py tests/session/memory/test_streaming_memory_updater.py
|
||||
uv run ruff format --check openviking/session/memory/streaming_memory_updater.py tests/session/memory/test_streaming_memory_updater.py
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: all tests and checks pass with no warnings introduced by the change.
|
||||
|
||||
- [x] **Step 7: Commit the implementation**
|
||||
|
||||
```bash
|
||||
git add openviking/session/memory/streaming_memory_updater.py tests/session/memory/test_streaming_memory_updater.py docs/superpowers/plans/2026-08-10-memory-overview-lock-coverage.md
|
||||
git commit -m "fix(memory): cover overview files in update leases"
|
||||
```
|
||||
@@ -1,268 +0,0 @@
|
||||
# Memory Link Lock Stabilization Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Ensure a streaming memory update acquires one stable exact-path lease covering relationship endpoints discovered from persisted replacement sources before any mutation begins.
|
||||
|
||||
**Architecture:** Add a pre-apply lease helper that acquires the current operation paths, rereads persisted deleted memories under that lease, and releases/reacquires the complete batch whenever new endpoints appear. The helper revalidates after every reacquisition and aborts after three acquisitions, while `MemoryUpdater.apply_operations` continues to receive and release only the final stable lease.
|
||||
|
||||
**Tech Stack:** Python 3.10+, asyncio, pytest, Ruff, VikingFS/RagFS PathLock APIs.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Never wait for newly discovered paths while retaining a narrower lease.
|
||||
- Do not call `MemoryUpdater.apply_operations` until the lock set is stable.
|
||||
- Preserve exact locks, deterministic path sorting, overview lock coverage, and the existing 300-second acquisition timeout.
|
||||
- Treat persisted memory content as authoritative only for link endpoint discovery; do not replace extraction output.
|
||||
- Limit stabilization to three total batch acquisitions and release the current lease before propagating failure.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Stabilize authoritative relationship lock coverage before mutation
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/session/memory/test_streaming_memory_updater.py:43-110,385-470`
|
||||
- Modify: `openviking/session/memory/streaming_memory_updater.py:450-490,1840-1890`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `_operation_lock_paths(operations, viking_fs, ctx) -> list[str]`, `_uri_lock_paths(uris, viking_fs, ctx) -> list[str]`, `MemoryFileUtils.read(content, uri=uri) -> MemoryFile`, and `ResolvedOperations.delete_replacements`.
|
||||
- Produces: `_acquire_stable_operation_lease(operations, viking_fs, ctx) -> Any | None`, returning a lease whose requested path set covers every persisted replacement-source link and backlink endpoint observed under that lease, or raising before mutation after three expanding observations.
|
||||
|
||||
- [x] **Step 1: Write all failing stabilization tests**
|
||||
|
||||
Extend `RecordingPathlockClient` so each acquisition returns a distinct lease while preserving the first lease value expected by existing tests:
|
||||
|
||||
```python
|
||||
lease_number = len([event for event in self.events if event[0] == "acquire"]) + 1
|
||||
lease_ref = "memory-batch-lease" if lease_number == 1 else f"memory-batch-lease-{lease_number}"
|
||||
lease = {"lease_ref": lease_ref}
|
||||
```
|
||||
|
||||
Add a `ChangingReadPathlockedInMemoryVikingFS` fake for TOCTOU cases:
|
||||
|
||||
```python
|
||||
class ChangingReadPathlockedInMemoryVikingFS(PathlockedInMemoryVikingFS):
|
||||
def __init__(self, files, changing_uri, changing_contents):
|
||||
super().__init__(files)
|
||||
self.changing_uri = changing_uri
|
||||
self.changing_contents = list(changing_contents)
|
||||
self.changing_read_count = 0
|
||||
|
||||
async def read_file(self, uri: str, ctx=None):
|
||||
if uri == self.changing_uri and self.changing_read_count < len(self.changing_contents):
|
||||
content = self.changing_contents[self.changing_read_count]
|
||||
self.changing_read_count += 1
|
||||
self.events.append(("read", uri, self._async_agfs.active_lease))
|
||||
return content
|
||||
return await super().read_file(uri, ctx=ctx)
|
||||
```
|
||||
|
||||
Add `test_streaming_memory_updater_reacquires_for_persisted_delete_links_before_writes`. Build a persisted deleted `MemoryFile` whose `links` contains a neighbor absent from the `delete_file_contents` fixture, then call `_apply_operations` and assert:
|
||||
|
||||
```python
|
||||
assert neighbor_path not in first_acquire[1]
|
||||
assert neighbor_path in second_acquire[1]
|
||||
assert events.index(first_release) < events.index(second_acquire)
|
||||
assert all(event[2] == {"lease_ref": "memory-batch-lease-2"} for event in write_events)
|
||||
assert events.index(second_acquire) < min(events.index(event) for event in write_events)
|
||||
```
|
||||
|
||||
This test catches removal of authoritative persisted-link discovery, failure to release before expansion, and writes performed under the narrow lease.
|
||||
|
||||
Add `test_streaming_memory_updater_revalidates_changed_persisted_links`. Return persisted content containing neighbor A on the first read and content containing both A and B on subsequent reads. Assert exactly three acquisitions, both neighbor paths in the third batch, and every write event uses `{"lease_ref": "memory-batch-lease-3"}`.
|
||||
|
||||
Add `test_streaming_memory_updater_aborts_after_three_expanding_lock_acquisitions`. Return three successive persisted contents that add neighbors A, B, and C. Assert:
|
||||
|
||||
```python
|
||||
with pytest.raises(RuntimeError, match="after 3 acquisitions"):
|
||||
await updater._apply_operations(
|
||||
operations=operations,
|
||||
request=request,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
assert len([event for event in fs.events if event[0] == "acquire"]) == 3
|
||||
assert len([event for event in fs.events if event[0] == "release"]) == 3
|
||||
assert fs.writes == []
|
||||
```
|
||||
|
||||
- [x] **Step 2: Run the new tests and verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py -k 'reacquires_for_persisted_delete_links_before_writes or revalidates_changed_persisted_links or aborts_after_three_expanding_lock_acquisitions' -q
|
||||
```
|
||||
|
||||
Expected: all three tests FAIL because the current implementation acquires only once and never discovers persisted endpoints before mutation.
|
||||
|
||||
- [x] **Step 3: Implement persisted endpoint discovery and stable acquisition**
|
||||
|
||||
Add a constant and two module-level helpers near the existing lock-path helpers:
|
||||
|
||||
```python
|
||||
_MEMORY_APPLY_LOCK_MAX_ACQUISITIONS = 3
|
||||
|
||||
|
||||
async def _persisted_replacement_relation_uris(
|
||||
operations: ResolvedOperations,
|
||||
viking_fs: Any,
|
||||
ctx: RequestContext,
|
||||
) -> set[str]:
|
||||
uris: set[str] = set()
|
||||
for deleted_uri in dict(operations.delete_replacements or {}):
|
||||
try:
|
||||
content = await viking_fs.read_file(deleted_uri, ctx=ctx)
|
||||
except (FileNotFoundError, NotFoundError):
|
||||
continue
|
||||
if not content:
|
||||
continue
|
||||
memory_file = MemoryFileUtils.read(content, uri=deleted_uri)
|
||||
for link in list(memory_file.links or []) + list(memory_file.backlinks or []):
|
||||
from_uri = link.get("from_uri") if isinstance(link, dict) else link.from_uri
|
||||
to_uri = link.get("to_uri") if isinstance(link, dict) else link.to_uri
|
||||
if from_uri:
|
||||
uris.add(str(from_uri))
|
||||
if to_uri:
|
||||
uris.add(str(to_uri))
|
||||
return uris
|
||||
```
|
||||
|
||||
Implement `_acquire_stable_operation_lease` with a monotonic `required_paths` set:
|
||||
|
||||
```python
|
||||
async def _acquire_stable_operation_lease(
|
||||
operations: ResolvedOperations,
|
||||
viking_fs: Any | None,
|
||||
ctx: RequestContext,
|
||||
) -> Any | None:
|
||||
lock_paths = _operation_lock_paths(operations, viking_fs, ctx)
|
||||
if not lock_paths:
|
||||
return None
|
||||
|
||||
required_paths = set(lock_paths)
|
||||
for acquisition in range(1, _MEMORY_APPLY_LOCK_MAX_ACQUISITIONS + 1):
|
||||
lease = await viking_fs._async_agfs.pathlock_acquire_exact_batch(
|
||||
sorted(required_paths),
|
||||
timeout_secs=_MEMORY_APPLY_LOCK_TIMEOUT_SECONDS,
|
||||
)
|
||||
try:
|
||||
relation_uris = await _persisted_replacement_relation_uris(
|
||||
operations,
|
||||
viking_fs,
|
||||
ctx,
|
||||
)
|
||||
expanded_paths = required_paths | set(
|
||||
_uri_lock_paths(relation_uris, viking_fs, ctx)
|
||||
)
|
||||
except BaseException:
|
||||
await viking_fs._async_agfs.pathlock_release(lease)
|
||||
raise
|
||||
|
||||
if expanded_paths == required_paths:
|
||||
return lease
|
||||
|
||||
await viking_fs._async_agfs.pathlock_release(lease)
|
||||
required_paths = expanded_paths
|
||||
if acquisition == _MEMORY_APPLY_LOCK_MAX_ACQUISITIONS:
|
||||
raise RuntimeError(
|
||||
"Unable to stabilize memory apply lock coverage after "
|
||||
f"{_MEMORY_APPLY_LOCK_MAX_ACQUISITIONS} acquisitions"
|
||||
)
|
||||
|
||||
raise AssertionError("unreachable")
|
||||
```
|
||||
|
||||
Import `NotFoundError` from `openviking_cli.exceptions`. Catch only
|
||||
`FileNotFoundError` and `NotFoundError`; parsing and permission failures must
|
||||
abort before mutation.
|
||||
|
||||
Change `_apply_operations` from its direct `pathlock_acquire_exact_batch` call to:
|
||||
|
||||
```python
|
||||
lease = await _acquire_stable_operation_lease(
|
||||
operations,
|
||||
viking_fs,
|
||||
request.ctx,
|
||||
)
|
||||
```
|
||||
|
||||
Retain the existing final release around `MemoryUpdater.apply_operations`.
|
||||
|
||||
- [x] **Step 4: Run the new tests and verify GREEN**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py -k 'reacquires_for_persisted_delete_links_before_writes or revalidates_changed_persisted_links or aborts_after_three_expanding_lock_acquisitions' -q
|
||||
```
|
||||
|
||||
Expected: 3 passed.
|
||||
|
||||
- [x] **Step 5: Run the existing lease regression test**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py::test_streaming_memory_updater_holds_batch_pathlock_across_apply -q
|
||||
```
|
||||
|
||||
Expected: 1 passed and exactly one acquisition remains for an operation without replacement inheritance.
|
||||
|
||||
- [x] **Step 6: Run focused regressions and static checks**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/session/memory/test_streaming_memory_updater.py tests/session/memory/test_memory_updater.py -q
|
||||
uv run ruff check openviking/session/memory/streaming_memory_updater.py tests/session/memory/test_streaming_memory_updater.py
|
||||
uv run ruff format --check openviking/session/memory/streaming_memory_updater.py tests/session/memory/test_streaming_memory_updater.py
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: all commands exit 0 without new warnings.
|
||||
|
||||
- [x] **Step 7: Commit the implementation**
|
||||
|
||||
```bash
|
||||
git add openviking/session/memory/streaming_memory_updater.py tests/session/memory/test_streaming_memory_updater.py docs/superpowers/plans/2026-08-11-memory-link-lock-stabilization.md
|
||||
git commit -m "fix(memory): stabilize link update lock coverage"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Lock remapped post-group link endpoints
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/session/memory/test_streaming_memory_updater.py`
|
||||
- Modify: `openviking/session/memory/streaming_memory_updater.py:231-272`
|
||||
|
||||
**Behavior:** `_apply_post_group_links` must acquire its exact-path batch lease
|
||||
from the endpoints produced by `remap_stored_links`, not the original request
|
||||
endpoints. `filter_valid_links` may reduce that set but cannot introduce an
|
||||
endpoint outside it.
|
||||
|
||||
- [x] **Step 1: Add the failing regression test**
|
||||
|
||||
Create a grouped-link scenario with original lower-case endpoint URIs and
|
||||
`result.operations.delete_replacements` mapping them to differently cased
|
||||
replacement URIs. Use the pathlock-aware VikingFS fake so a write outside the
|
||||
active lease raises the real coverage-style error. Assert the replacement
|
||||
paths are acquired and the remapped endpoint files are updated.
|
||||
|
||||
- [x] **Step 2: Verify RED**
|
||||
|
||||
Run only the new test and confirm it fails because the old implementation locks
|
||||
the original paths before remapping.
|
||||
|
||||
- [x] **Step 3: Implement the ordering fix**
|
||||
|
||||
Move `remap_stored_links` before `_uri_lock_paths` in
|
||||
`_apply_post_group_links`. Keep merge, filtering, result accounting, timeout,
|
||||
and lease release behavior unchanged.
|
||||
|
||||
- [x] **Step 4: Verify GREEN and focused regressions**
|
||||
|
||||
Run the new test, the existing post-group link tests, the focused streaming
|
||||
memory updater suite, Ruff checks for the touched Python files, and
|
||||
`git diff --check`.
|
||||
@@ -1,162 +0,0 @@
|
||||
# SessionCommit Default 8 with Local Override 50 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make unconfigured SessionCommit workers default to 8 while configuring this developer's local OpenViking instance to use 50.
|
||||
|
||||
**Architecture:** `QueueWorkersConfig` remains the server-facing configuration source and explicit values continue through `OpenVikingService` into `QueueManager`. Repository fallbacks and documentation use 8; `~/.openviking/ov.conf` supplies a local explicit override of 50 without entering Git.
|
||||
|
||||
**Tech Stack:** Python 3.13, Pydantic v2, pytest, Ruff, JSON configuration
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Change only the `SessionCommit` queue default; other queue defaults remain 4.
|
||||
- `examples/ov.conf.example` explicitly shows 8.
|
||||
- `~/.openviking/ov.conf` explicitly sets 50 and preserves every existing setting.
|
||||
- Do not restart the running OpenViking service.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Repository defaults and documentation
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/test_config_loader.py:125-146`
|
||||
- Modify: `tests/storage/test_queue_manager.py:21-24`
|
||||
- Modify: `openviking_cli/utils/config/queue_worker_config.py:20-29`
|
||||
- Modify: `openviking/storage/queuefs/queue_manager.py:24-97`
|
||||
- Modify: `openviking/service/core.py:146-157`
|
||||
- Modify: `examples/ov.conf.example:85-89`
|
||||
- Modify: `docs/en/configuration/01-server.md:217-221`
|
||||
- Modify: `docs/zh/configuration/01-server.md:217-221`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `OpenVikingConfig.from_dict(data: dict) -> OpenVikingConfig` and `QueueManager(..., max_concurrent_session_commit: int = 8)`.
|
||||
- Produces: an unconfigured `session_commit.max_concurrent` of 8 and preservation of explicit values such as 50.
|
||||
|
||||
- [ ] **Step 1: Change the behavior tests to require default 8 and explicit 50**
|
||||
|
||||
In `tests/test_config_loader.py`, make the default assertion and explicit override read:
|
||||
|
||||
```python
|
||||
assert config.queue_workers.session_commit.max_concurrent == 8
|
||||
|
||||
# In test_queue_worker_concurrency_accepts_separate_values:
|
||||
"session_commit": {"max_concurrent": 50},
|
||||
assert config.queue_workers.session_commit.max_concurrent == 50
|
||||
```
|
||||
|
||||
In `tests/storage/test_queue_manager.py`, rename and update the focused test:
|
||||
|
||||
```python
|
||||
def test_session_commit_concurrent_defaults_to_eight() -> None:
|
||||
manager = QueueManager(agfs=object(), max_concurrent_external_parse=9)
|
||||
|
||||
assert manager._max_concurrent_for_queue(manager.SESSION_COMMIT) == 8
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused tests to verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/storage/test_queue_manager.py tests/test_config_loader.py -q --no-cov
|
||||
```
|
||||
|
||||
Expected: the two default-value assertions fail with actual value 50; the explicit value 50 assertion passes.
|
||||
|
||||
- [ ] **Step 3: Implement the repository default of 8**
|
||||
|
||||
Use 8 for `DEFAULT_MAX_CONCURRENT_SESSION_COMMIT` in `queue_manager.py`, for the `session_commit` default factory in `queue_worker_config.py`, and for `OpenVikingService._init_storage()`'s fallback. Keep the existing explicit-value data flow unchanged.
|
||||
|
||||
```python
|
||||
DEFAULT_MAX_CONCURRENT_SESSION_COMMIT = 8
|
||||
|
||||
session_commit: QueueWorkerConfig = Field(
|
||||
default_factory=lambda: QueueWorkerConfig(max_concurrent=8)
|
||||
)
|
||||
|
||||
max_concurrent_session_commit: int = 8,
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Update repository examples and documentation**
|
||||
|
||||
Set `examples/ov.conf.example` to:
|
||||
|
||||
```json
|
||||
"session_commit": {"max_concurrent": 8}
|
||||
```
|
||||
|
||||
Set the English and Chinese `queue_workers.session_commit.max_concurrent` default tables to `8`.
|
||||
|
||||
- [ ] **Step 5: Run focused tests and static checks**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/storage/test_queue_manager.py tests/test_config_loader.py tests/unit/service/test_core_consistency.py -q --no-cov
|
||||
uv run ruff check openviking/storage/queuefs/queue_manager.py openviking/service/core.py openviking_cli/utils/config/queue_worker_config.py tests/storage/test_queue_manager.py tests/test_config_loader.py tests/unit/service/test_core_consistency.py
|
||||
uv run ruff format --check openviking/storage/queuefs/queue_manager.py openviking/service/core.py openviking_cli/utils/config/queue_worker_config.py tests/storage/test_queue_manager.py tests/test_config_loader.py tests/unit/service/test_core_consistency.py
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: 44 tests pass, Ruff reports no errors or formatting changes, and Git reports no whitespace errors.
|
||||
|
||||
- [ ] **Step 6: Commit the repository change**
|
||||
|
||||
```bash
|
||||
git add docs/en/configuration/01-server.md docs/zh/configuration/01-server.md examples/ov.conf.example openviking/service/core.py openviking/storage/queuefs/queue_manager.py openviking_cli/utils/config/queue_worker_config.py tests/storage/test_queue_manager.py tests/test_config_loader.py
|
||||
git commit -m "perf(queue): default session commit concurrency to 8"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Local OpenViking override
|
||||
|
||||
**Files:**
|
||||
- Modify outside Git: `~/.openviking/ov.conf`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: the root JSON object in `~/.openviking/ov.conf`.
|
||||
- Produces: `queue_workers.session_commit.max_concurrent == 50` while every unrelated JSON value remains identical.
|
||||
|
||||
- [ ] **Step 1: Create a protected temporary copy and backup**
|
||||
|
||||
Run these exact commands. The first command prevents overwriting an earlier backup:
|
||||
|
||||
```bash
|
||||
test ! -e /tmp/openviking-ovconf-session-commit-50
|
||||
mkdir /tmp/openviking-ovconf-session-commit-50
|
||||
cp ~/.openviking/ov.conf /tmp/openviking-ovconf-session-commit-50/ov.conf.edit
|
||||
cp ~/.openviking/ov.conf /tmp/openviking-ovconf-session-commit-50/ov.conf.backup
|
||||
```
|
||||
|
||||
Do not print either file's full contents.
|
||||
|
||||
- [ ] **Step 2: Insert the local override with apply_patch**
|
||||
|
||||
Patch the temporary `ov.conf.edit` immediately before the existing root-level `embedding` section:
|
||||
|
||||
```json
|
||||
"queue_workers": {
|
||||
"session_commit": {"max_concurrent": 50}
|
||||
},
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Validate the edit before copying it back**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
/usr/bin/python3 -c 'import copy, json, pathlib; root=pathlib.Path("/tmp/openviking-ovconf-session-commit-50"); before=json.loads((root/"ov.conf.backup").read_text()); after=json.loads((root/"ov.conf.edit").read_text()); expected=copy.deepcopy(before); expected.setdefault("queue_workers", {}).setdefault("session_commit", {})["max_concurrent"]=50; assert after == expected; print("local_config_valid=true")'
|
||||
```
|
||||
|
||||
Expected output: `local_config_valid=true`. This proves unrelated local settings were preserved without printing secrets.
|
||||
|
||||
- [ ] **Step 4: Install and verify the local configuration**
|
||||
|
||||
Copy the validated temporary edit to `~/.openviking/ov.conf`, preserving the original as `ov.conf.backup` in the temporary directory. Parse the installed file and print only `queue_workers.session_commit.max_concurrent`; expected output is `50`. Do not restart OpenViking.
|
||||
|
||||
```bash
|
||||
cp /tmp/openviking-ovconf-session-commit-50/ov.conf.edit ~/.openviking/ov.conf
|
||||
/usr/bin/python3 -c 'import json, pathlib; data=json.loads((pathlib.Path.home()/".openviking"/"ov.conf").read_text()); print(data["queue_workers"]["session_commit"]["max_concurrent"])'
|
||||
```
|
||||
@@ -1,70 +0,0 @@
|
||||
# Memory Overview Lock Coverage
|
||||
|
||||
## Problem
|
||||
|
||||
`StreamingMemoryUpdater` currently acquires one exact-path batch lease for the
|
||||
memory files, replacement targets, and link endpoints touched by an update. It
|
||||
passes that lease into `MemoryUpdater`, which later reuses it to write each
|
||||
affected directory's derived `.overview.md` file.
|
||||
|
||||
The overview path is not part of the original lock batch. Rust pathlock coverage
|
||||
validation therefore rejects the write with `does not cover the requested
|
||||
operation`, leaving the memory file updated but its overview stale or missing.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add each affected `.overview.md` path to the original exact-path batch. This
|
||||
preserves one atomic lease acquisition, serializes updates that share an
|
||||
overview, and avoids locking unrelated descendants.
|
||||
2. Acquire a separate exact lock inside `generate_overview`. This holds the
|
||||
overview lock for less time, but introduces nested acquisition and permits
|
||||
memory files to change before overview generation obtains its lock.
|
||||
3. Replace the exact file locks with directory tree locks. This covers every
|
||||
derived operation but unnecessarily serializes all mutations below a memory
|
||||
directory.
|
||||
|
||||
## Design
|
||||
|
||||
Use option 1. Extend `_operation_lock_paths` so every upserted or deleted memory
|
||||
URI contributes both its own exact path and its parent directory's
|
||||
`.overview.md` exact path. Continue adding replacement and link endpoint paths
|
||||
as today; those files do not independently trigger overview generation, so they
|
||||
do not contribute additional overview paths unless they are also an upsert or
|
||||
delete target.
|
||||
|
||||
Normalize directory URIs by removing trailing slashes before appending
|
||||
`.overview.md`, convert all URIs through `VikingFS._uri_to_path`, and retain the
|
||||
existing set-based deduplication and sorted result. Multiple operations in one
|
||||
directory therefore add only one overview lock.
|
||||
|
||||
The resulting batch lease is acquired before any memory mutation and remains
|
||||
held until `MemoryUpdater.apply_operations` finishes. The existing
|
||||
`lease_ref=self._transaction_handle` propagation then becomes valid for the
|
||||
overview write without changes to `generate_overview`.
|
||||
|
||||
This change intentionally uses an exact lock for `.overview.md`, not a tree lock
|
||||
for its directory. It fixes overview write coverage while preserving concurrency
|
||||
for unrelated files. Recursive removal of an empty memory directory requires a
|
||||
tree lock and is outside this focused change; its current best-effort behavior
|
||||
remains unchanged.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Lock acquisition retains the existing 300-second timeout and all-or-nothing
|
||||
batch semantics. If the overview path conflicts with another updater, the whole
|
||||
memory update waits or fails before mutating storage rather than updating memory
|
||||
and discovering invalid coverage at overview write time.
|
||||
|
||||
## Verification
|
||||
|
||||
Add regression assertions covering:
|
||||
|
||||
- one upsert includes its memory file and sibling `.overview.md` paths;
|
||||
- multiple operations in the same directory deduplicate the overview path;
|
||||
- operations in distinct directories include one overview path per directory;
|
||||
- delete targets contribute their overview paths;
|
||||
- replacement and link endpoint coverage remains unchanged.
|
||||
|
||||
Run the focused streaming-memory-updater tests, the memory-updater overview
|
||||
tests, formatting/lint checks for touched Python files, and the broader session
|
||||
memory test suite if focused tests pass.
|
||||
@@ -1,52 +0,0 @@
|
||||
# Service Package Circular Import Fix
|
||||
|
||||
## Problem
|
||||
|
||||
Importing a session training submodule in a fresh Python process currently fails:
|
||||
|
||||
```python
|
||||
from openviking.session.train.components.progress import ProgressSummaryColumn
|
||||
```
|
||||
|
||||
The import enters `openviking.storage.queuefs`, whose `named_queue` module imports
|
||||
`openviking.service.task_work_index`. Python initializes the parent
|
||||
`openviking.service` package first. Its eager exports import `resource_service`,
|
||||
which asks the still-partially-initialized QueueFS package for `QueueManager` and
|
||||
raises a circular-import error.
|
||||
|
||||
## Options
|
||||
|
||||
1. Make `openviking.service` exports lazy. This follows the existing patterns in
|
||||
`openviking.core`, `openviking.storage`, and `openviking.client`, preserves the
|
||||
public API, and changes only the package entry point.
|
||||
2. Move `task_work_index` into QueueFS and retain a compatibility shim. This
|
||||
improves dependency direction but touches many imports and broadens the fix.
|
||||
3. Defer the imports inside QueueFS methods. This breaks the immediate cycle but
|
||||
hides the dependency until runtime and is harder to maintain.
|
||||
|
||||
## Design
|
||||
|
||||
Use option 1. Replace eager imports in `openviking/service/__init__.py` with:
|
||||
|
||||
- `TYPE_CHECKING` imports for static analysis;
|
||||
- a map from each existing public name to its defining module;
|
||||
- module-level `__getattr__` that imports and caches an export on first access;
|
||||
- `__dir__` and the unchanged `__all__` list for discoverability and compatibility.
|
||||
|
||||
No service class, QueueFS implementation, or benchmark code changes. Existing
|
||||
imports such as `from openviking.service import OpenVikingService` retain their
|
||||
behavior, while importing `openviking.service.task_work_index` no longer loads
|
||||
the whole service layer.
|
||||
|
||||
## Verification
|
||||
|
||||
Add an isolated-process regression test so prior imports in the pytest process
|
||||
cannot mask the order-dependent bug. The subprocess will verify, in a fresh
|
||||
interpreter:
|
||||
|
||||
- the LoCoMo-triggering `ProgressSummaryColumn` import;
|
||||
- direct `QueueManager` and `VikingDBManager` imports;
|
||||
- existing top-level Service exports and service-submodule imports.
|
||||
|
||||
Run the focused regression test, relevant service/QueueFS tests, Ruff, and the
|
||||
original import command before committing.
|
||||
@@ -1,98 +0,0 @@
|
||||
# First Failing Patch Block Diagnostic Design
|
||||
|
||||
## Context
|
||||
|
||||
`ExtractLoop._validate_patch_operations()` currently applies every SEARCH/REPLACE block in a field as one `StrPatch`. If any later block fails, the validator catches the field-level failure and then reports the first non-empty block instead of the block that actually failed.
|
||||
|
||||
This produces misleading repair prompts. In observed traces, an early block matched exactly once while a later `- Values friendship and compassion` block matched twice. The repair prompt identified the valid early block, so the model modified that block and left the duplicate SEARCH unchanged. The second validation failed for the same underlying reason.
|
||||
|
||||
## Goal
|
||||
|
||||
Report the first block that actually fails in patch execution order, with enough structured information for the repair model to correct it.
|
||||
|
||||
The change must preserve:
|
||||
|
||||
- `PatchOp` matching and application behavior, including fuzzy fallback;
|
||||
- sequential block semantics, where an earlier replacement can affect a later SEARCH;
|
||||
- plain-content validation introduced for rendered Markdown links;
|
||||
- the existing single repair retry limit;
|
||||
- existing empty-SEARCH and SEARCH-equals-REPLACE handling;
|
||||
- cross-file SEARCH diagnostics.
|
||||
|
||||
## Selected Approach
|
||||
|
||||
Validate each active block sequentially through the existing `PatchOp` implementation.
|
||||
|
||||
For each operation field containing a `StrPatch`:
|
||||
|
||||
1. Initialize `working_content` from `operation.old_memory_file_content.plain_content()`.
|
||||
2. Iterate over blocks in output order.
|
||||
3. Skip blocks that the existing patch implementation treats as inactive, including empty SEARCH and SEARCH equal to REPLACE.
|
||||
4. Wrap the current block in a one-block `StrPatch` and apply it to `working_content` with `PatchOp.apply()`.
|
||||
5. If it succeeds and changes the content, retain the returned value as the next `working_content` and continue.
|
||||
6. If it raises or leaves the content unchanged, report that block as the first actual failure and stop validating the field.
|
||||
7. If all active blocks apply, return no validation error for the field.
|
||||
|
||||
This mirrors real patch execution without duplicating the patch handler's exact, fuzzy, line-based, marker-unescaping, or sequencing behavior inside `ExtractLoop`.
|
||||
|
||||
## Error Shape
|
||||
|
||||
The existing error fields remain, with these additions:
|
||||
|
||||
```json
|
||||
{
|
||||
"uri": "viking://user/default/peers/conv-26/memories/profile.md",
|
||||
"page_id": 1,
|
||||
"field": "content",
|
||||
"block_index": 8,
|
||||
"search": "- Values friendship and compassion",
|
||||
"reason": "non_unique",
|
||||
"match_count": 2,
|
||||
"found_in_other_uris": []
|
||||
}
|
||||
```
|
||||
|
||||
`block_index` is one-based so it is easy to relate to model output.
|
||||
|
||||
`reason` is classified as:
|
||||
|
||||
- `non_unique` when the current working content contains the effective SEARCH more than once;
|
||||
- `not_found` when it contains the effective SEARCH zero times and application makes no change;
|
||||
- `not_applied` for any other unchanged result;
|
||||
- `apply_error` for an exception that cannot be classified by the exact match count.
|
||||
|
||||
The effective SEARCH used for `match_count` is `unescape_markers(block.search)`, matching the exact-search preprocessing in the patch handler.
|
||||
|
||||
`match_count` is calculated against the current sequential `working_content`, not the original file. It is diagnostic metadata; `PatchOp.apply()` remains authoritative for whether a block succeeds.
|
||||
|
||||
`found_in_other_uris` continues to compare against each read file's `plain_content()`. It is most useful for `not_found`, but remains present for a stable repair-prompt schema.
|
||||
|
||||
## Failure and Retry Behavior
|
||||
|
||||
Only the first actual failure for a field is reported because real patch execution cannot reliably evaluate later blocks after an earlier block fails. The repair prompt continues to request a complete regenerated operations object.
|
||||
|
||||
The retry policy is unchanged:
|
||||
|
||||
- the first failed validation adds the repair instruction and grants one extra iteration;
|
||||
- a second failed validation is logged but does not grant another repair iteration.
|
||||
|
||||
With accurate block diagnostics, the repair model receives the SEARCH it must change instead of an unrelated earlier block.
|
||||
|
||||
## Testing
|
||||
|
||||
Add focused tests to `TestExtractLoopPatchRepair`:
|
||||
|
||||
1. A valid first block followed by a duplicate second block reports the second block, `block_index = 2`, `reason = non_unique`, and `match_count = 2`.
|
||||
2. The repair response makes the duplicate SEARCH unique and completes after one repair iteration.
|
||||
3. A missing SEARCH reports `reason = not_found` and `match_count = 0`.
|
||||
4. Sequential validation applies an earlier successful replacement before checking the next block.
|
||||
5. All valid blocks complete without a repair retry.
|
||||
6. Existing Markdown-link plain-content regressions continue to pass.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Changing the number of allowed repair retries.
|
||||
- Changing prompt wording beyond exposing the new structured fields.
|
||||
- Reimplementing or simplifying `PatchOp` matching.
|
||||
- Changing memory storage, rendered links, or link metadata.
|
||||
- Reporting speculative errors in blocks after the first actual failure.
|
||||
@@ -1,161 +0,0 @@
|
||||
# Memory Link Lock Stabilization
|
||||
|
||||
## Problem
|
||||
|
||||
`StreamingMemoryUpdater` acquires one exact-path batch lease before calling
|
||||
`MemoryUpdater.apply_operations`. The lock set is computed from the resolved
|
||||
operations, including the links and backlinks present on
|
||||
`delete_file_contents`.
|
||||
|
||||
When a deleted memory is replaced, `MemoryUpdater` later rereads the persisted
|
||||
deleted file and inherits its links onto the replacement and neighboring
|
||||
memories. The persisted file may contain link endpoints that were absent from
|
||||
the resolved operation object. Those neighbors are therefore outside the
|
||||
original lease, and RagFS rejects their writes with `pathlock lease ref ...
|
||||
does not cover the requested operation`.
|
||||
|
||||
Lock expansion cannot safely happen inside relation inheritance. Upserts have
|
||||
already been written by that point, so releasing the original lease there
|
||||
would expose a partially applied update. Waiting for additional locks while
|
||||
holding the original lease could also produce a cross-owner circular wait.
|
||||
|
||||
## Options
|
||||
|
||||
1. Stabilize an exact-path lock set before applying any mutation. Acquire the
|
||||
initial operation locks, reread persisted replacement sources, calculate the
|
||||
complete relation endpoint set, and reacquire the complete batch if it grew.
|
||||
Revalidate after reacquisition and retry if the source relationships changed.
|
||||
2. Dynamically acquire additional exact locks during relation inheritance with
|
||||
the original lease as `owner_lease_ref`. This avoids self-conflict, but two
|
||||
owners can hold disjoint initial locks and wait for each other's expansion.
|
||||
It also occurs after earlier writes, so failure cannot restore atomicity.
|
||||
3. Acquire a tree lock for the entire peer memory subtree. This covers every
|
||||
possible relationship endpoint, but serializes otherwise independent memory
|
||||
updates and would significantly reduce Locomo commit concurrency.
|
||||
|
||||
Use option 1. It preserves exact-lock concurrency and makes the correctness
|
||||
boundary explicit: no memory mutation starts until one lease covers every path
|
||||
known from authoritative persisted relationship data.
|
||||
|
||||
## Design
|
||||
|
||||
Add a focused asynchronous helper in `streaming_memory_updater.py` that returns
|
||||
the stable lease used by `MemoryUpdater`:
|
||||
|
||||
```python
|
||||
async def _acquire_stable_operation_lease(
|
||||
operations: ResolvedOperations,
|
||||
viking_fs: Any,
|
||||
ctx: RequestContext,
|
||||
) -> Any | None:
|
||||
"""Acquire one lease covering authoritative relation endpoints."""
|
||||
```
|
||||
|
||||
The helper performs these steps:
|
||||
|
||||
1. Compute the initial exact paths with `_operation_lock_paths` and acquire them
|
||||
as one batch.
|
||||
2. While holding that lease, reread every URI in `delete_replacements`. Parse
|
||||
each persisted file and union all link and backlink endpoints into the
|
||||
required URI set.
|
||||
3. If the required lock set is unchanged, return the current lease.
|
||||
4. If it grew, release the current lease and acquire the full required set in
|
||||
one all-or-nothing batch. Do not wait for expanded paths while retaining the
|
||||
narrower lease.
|
||||
5. Reread the replacement sources after reacquisition and recompute the set. If
|
||||
the set grew again, release and repeat. Limit stabilization to three total
|
||||
batch acquisitions; failure raises before `MemoryUpdater.apply_operations`
|
||||
starts.
|
||||
|
||||
The authoritative reread is used only to discover relation endpoints. It does
|
||||
not replace the resolved operation objects or change the memory content chosen
|
||||
by extraction. Existing URI conversion, deduplication, deterministic sorting,
|
||||
overview coverage, and the 300-second batch acquisition timeout remain intact.
|
||||
|
||||
`StreamingMemoryUpdater._apply_operations` calls the helper inside its existing
|
||||
process-local `_apply_lock`, constructs `MemoryUpdater` with the returned lease,
|
||||
and releases that final lease in its existing `finally` block. No lock lifecycle
|
||||
logic is added to `_inherit_deleted_link_relations`.
|
||||
|
||||
## Concurrency and Consistency
|
||||
|
||||
Releasing a narrow lease before acquiring the complete set creates an unlocked
|
||||
window. The post-acquisition reread closes that TOCTOU gap: mutations proceed
|
||||
only when the relation endpoint set observed under the final lease is a subset
|
||||
of that lease's coverage. If another process changes a replacement source in
|
||||
the window, the changed endpoint set triggers another full-batch acquisition.
|
||||
|
||||
Each acquisition requests the complete known set at once. RagFS normalizes and
|
||||
sorts batch requests deterministically and applies all-or-nothing rollback on
|
||||
conflict. The updater never waits for an expanded path while retaining a
|
||||
narrower lease, eliminating the staged A-then-B/B-then-A circular-wait pattern.
|
||||
|
||||
The three-acquisition limit prevents an adversarially changing relationship
|
||||
graph from keeping a commit in an unbounded stabilization loop. On exhaustion,
|
||||
the helper releases its current lease and the update fails before writing any
|
||||
memory file. The helper also releases any lease it still owns before propagating
|
||||
an unexpected discovery or parsing exception.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- A missing persisted deleted file contributes no additional endpoints; the
|
||||
existing delete/update path remains responsible for reporting its result.
|
||||
- Malformed persisted memory content fails lock preparation and aborts the
|
||||
update before mutation rather than silently applying with incomplete locks.
|
||||
- Failure to acquire or release a lease propagates through the existing commit
|
||||
error path.
|
||||
- Stabilization exhaustion raises an explicit error that includes the number of
|
||||
acquisitions and does not call `MemoryUpdater.apply_operations`.
|
||||
|
||||
## Verification
|
||||
|
||||
Add regression coverage for these observable behaviors:
|
||||
|
||||
- a persisted replacement source containing a neighbor absent from
|
||||
`delete_file_contents` causes the final lease to include that neighbor before
|
||||
any write;
|
||||
- when the first authoritative reread expands the set, the narrow lease is
|
||||
released before the complete batch acquisition;
|
||||
- a source that changes during release and reacquisition is reread and causes a
|
||||
bounded second stabilization acquisition;
|
||||
- exhaustion raises before any memory write;
|
||||
- operations without replacement inheritance retain one acquisition and the
|
||||
existing lease propagation behavior.
|
||||
|
||||
Run the focused streaming memory updater tests, the memory updater regression
|
||||
tests, Ruff checks for touched files, and `git diff --check`.
|
||||
|
||||
## Follow-up: Post-group Link Remapping
|
||||
|
||||
Grouped streaming updates have a second link-write path in
|
||||
`_apply_post_group_links`. All grouped requests have completed before this
|
||||
method runs, so both inputs that determine the final link endpoints are already
|
||||
stable: the request's resolved links and the combined result's
|
||||
`delete_replacements` map.
|
||||
|
||||
The current ordering acquires exact locks for the original link endpoints and
|
||||
then remaps those endpoints. A case-normalizing replacement such as
|
||||
`entities/person/andrew.md` to `entities/person/Andrew.md` can therefore make
|
||||
`write_stored_links` write a path outside the lease.
|
||||
|
||||
Three approaches were considered:
|
||||
|
||||
1. Remap links first, then acquire exact locks for the remapped endpoints.
|
||||
This is the smallest fix and covers every later write because validation
|
||||
only filters links; it never changes or adds endpoints.
|
||||
2. Lock the union of original and remapped endpoints. This is correct but holds
|
||||
locks that the post-remap validation and write path never use.
|
||||
3. Acquire the original range and release/reacquire if remapping expands it.
|
||||
This adds an unnecessary unlocked window and retry lifecycle even though the
|
||||
complete remapped range is already computable.
|
||||
|
||||
Use option 1. `_apply_post_group_links` will merge and remap links before
|
||||
calling `_uri_lock_paths`. It will then acquire one exact-path batch lease,
|
||||
filter the remapped links, and write only endpoints already covered by that
|
||||
lease. No dynamic expansion or tree lock is needed on this path.
|
||||
|
||||
Add a regression test whose original links use pre-replacement URIs and whose
|
||||
combined result maps them to differently cased replacement URIs. The test must
|
||||
observe that the acquired batch contains the replacement paths and that all
|
||||
link writes complete under that lease. The test must fail against the old
|
||||
ordering by surfacing the same lease-coverage rejection seen in Locomo.
|
||||
@@ -1,23 +0,0 @@
|
||||
# Plain-Content Patch Validation Design
|
||||
|
||||
## Problem
|
||||
|
||||
The memory read tool exposes `MemoryFile.plain_content()` to the extraction model, so generated SEARCH text does not contain rendered Markdown links. The extract-loop pre-validation currently applies that SEARCH text to `MemoryFile.content`, which may contain rendered links. Valid patches are therefore rejected before the real updater runs, even though both updater paths already apply patches to `plain_content()`.
|
||||
|
||||
## Design
|
||||
|
||||
Align pre-validation with the established read and apply contract:
|
||||
|
||||
- Use `operation.old_memory_file_content.plain_content()` as the target text passed to `PatchOp.apply`.
|
||||
- Search other previously read files through `memory_file.plain_content()` when populating `found_in_other_uris`.
|
||||
- Keep persisted Markdown link rendering and link metadata unchanged. The updater continues to apply patches to plain content and render links during serialization.
|
||||
|
||||
No retry policy, patch algorithm, prompt, or storage format changes are included.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Existing validation behavior remains unchanged: an exception or unchanged result is reported as a repairable patch error. Only the text representation used for validation changes.
|
||||
|
||||
## Testing
|
||||
|
||||
Add a regression test with stored content containing a rendered Markdown link and a patch whose SEARCH text matches the plain visible form. Assert that pre-validation returns no patch errors. Existing invalid-patch tests continue to prove that genuinely missing SEARCH text still triggers one repair attempt.
|
||||
@@ -1,24 +0,0 @@
|
||||
# SessionCommit Default 8 with Local Override 50
|
||||
|
||||
## Goal
|
||||
|
||||
Change the repository-wide default `SessionCommit` worker concurrency from 4 to 8. Keep the developer's local OpenViking instance at 50 through an explicit `~/.openviking/ov.conf` override.
|
||||
|
||||
## Repository behavior
|
||||
|
||||
An installation that does not configure `queue_workers.session_commit.max_concurrent` uses 8. The configuration model, `QueueManager` constructor and initializer, and `OpenVikingService` storage fallback all use the same value so different construction paths cannot silently select different limits.
|
||||
|
||||
`examples/ov.conf.example` explicitly shows 8, and the English and Chinese server configuration references document 8 as the default. Other queue concurrency defaults remain unchanged.
|
||||
|
||||
An explicit configuration value continues to take precedence. Existing validation still rejects zero and negative concurrency values.
|
||||
|
||||
## Local behavior
|
||||
|
||||
Update only `queue_workers.session_commit.max_concurrent` in `~/.openviking/ov.conf` to 50, preserving all other local configuration. This local override is not committed to the repository. Do not restart the running OpenViking service as part of this change; the new local value takes effect on its next restart.
|
||||
|
||||
## Testing
|
||||
|
||||
- Verify an empty `OpenVikingConfig` selects 8 for `session_commit` while the other queue defaults remain 4.
|
||||
- Verify `QueueManager` direct construction selects 8 for `SessionCommit`.
|
||||
- Verify an explicit `session_commit.max_concurrent` value, including 50, is preserved.
|
||||
- Run focused configuration, queue-manager, and service consistency tests plus Ruff and whitespace checks.
|
||||
@@ -292,11 +292,11 @@ commit 策略。如果后续批次或写入后的 commit 失败,`OpenVikingPar
|
||||
仓库内提供了可直接运行的最小示例,使用内存测试客户端,无需模型凭证:
|
||||
|
||||
```bash
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langchain/rag/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langchain/context-backend/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langchain/message-history/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langgraph/agent/quick_app.py
|
||||
uv run --project integrations/langchain --extra langgraph python examples/langchain-langgraph/langgraph/middleware/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langchain/rag/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langchain/context-backend/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langchain/message-history/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langgraph/agent/quick_app.py
|
||||
uv run --project examples/langchain --extra langgraph python examples/langchain-langgraph/langgraph/middleware/quick_app.py
|
||||
```
|
||||
|
||||
连接真实 OpenViking 服务和 OpenAI 兼容模型的示例见 [live LangGraph app](https://github.com/volcengine/OpenViking/blob/main/examples/langchain-langgraph/langgraph/agent/live_app.py)。
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Mapping
|
||||
|
||||
SCM_TAG_REGEX = r"^v(?P<version>[0-9]+(?:\.[0-9]+)*)$"
|
||||
SCM_GIT_DESCRIBE_COMMAND = "git describe --dirty --tags --long --match v[0-9]*"
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
|
||||
def _get_scm_version(project_root: Path) -> str:
|
||||
@@ -22,10 +22,10 @@ if str(SETUP_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SETUP_DIR))
|
||||
|
||||
get_host_engine_build_config = importlib.import_module(
|
||||
"build_support.x86_profiles"
|
||||
"scripts.build_support.x86_profiles"
|
||||
).get_host_engine_build_config
|
||||
resolve_openviking_version = importlib.import_module(
|
||||
"build_support.versioning"
|
||||
"scripts.build_support.versioning"
|
||||
).resolve_openviking_version
|
||||
|
||||
CMAKE_PATH = shutil.which("cmake") or "cmake"
|
||||
|
||||
@@ -50,7 +50,7 @@ def test_build_docker_workflow_does_not_force_zero_version_on_main_builds():
|
||||
|
||||
assert "fetch-depth: 0" in workflow
|
||||
assert "id: openviking-version" in workflow
|
||||
assert "from build_support.versioning import resolve_openviking_version" in workflow
|
||||
assert "from scripts.build_support.versioning import resolve_openviking_version" in workflow
|
||||
assert "OPENVIKING_VERSION=${{ steps.openviking-version.outputs.version }}" in workflow
|
||||
assert zero_build_arg not in workflow
|
||||
assert "fallback to 0.0.0" not in workflow
|
||||
|
||||
@@ -26,7 +26,7 @@ def test_python_sdk_versioning_uses_sdk_only_at_sign_tags() -> None:
|
||||
|
||||
|
||||
def test_build_support_versioning_uses_main_release_tags_only(monkeypatch) -> None:
|
||||
from build_support import versioning
|
||||
from scripts.build_support import versioning
|
||||
|
||||
captured_kwargs = {}
|
||||
fake_setuptools_scm = ModuleType("setuptools_scm")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from build_support.x86_profiles import get_host_engine_build_config
|
||||
from scripts.build_support.x86_profiles import get_host_engine_build_config
|
||||
|
||||
|
||||
def test_x86_host_uses_sse3_extension_baseline():
|
||||
|
||||