mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
fix: harden the 0.4.1.7 release configuration (#456)
Some checks failed
NPM Format / Website checks (push) Has been cancelled
GitHub Pages Check / test-and-build (push) Has been cancelled
Package Check / distributions (push) Has been cancelled
Deploy ReMe documentation / build (push) Has been cancelled
Windows Smoke / CLI smoke - py3.11 (push) Has been cancelled
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Tests ReMe / Unit Tests - py3.11 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.12 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.13 (push) Has been cancelled
Deploy ReMe documentation / deploy (push) Has been cancelled
Some checks failed
NPM Format / Website checks (push) Has been cancelled
GitHub Pages Check / test-and-build (push) Has been cancelled
Package Check / distributions (push) Has been cancelled
Deploy ReMe documentation / build (push) Has been cancelled
Windows Smoke / CLI smoke - py3.11 (push) Has been cancelled
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Tests ReMe / Unit Tests - py3.11 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.12 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.13 (push) Has been cancelled
Deploy ReMe documentation / deploy (push) Has been cancelled
* fix(packaging): harden the Studio release workflow * chore(daily-paper): tune scheduled discovery defaults * fix(docs): link the ReMe blog to GitHub Pages * fix(docs): increase Chinese hero title spacing * refactor(docs): share hero title line spacing * fix(docs): keep desktop hero copy on two lines * fix(docs): widen the home hero description * style(docs): loosen hero title line height * fix(docs): hide Markdown frontmatter in rendered pages * docs(readme): simplify installation and remove standalone ReMe Studio instructions - Remove references to separate ReMe Studio package and static build steps - Clarify that `core` extra includes common integrations including Studio - Update installation instructions to use `pip install -e ".[core]"` - Remove detailed Studio usage and frontend development instructions - Note that Studio is included with `core` and optional via `web` extra - Simplify Quick Start guide by removing Studio usage step - Remove mentions of serving Studio with HTTP service when using extras - Update both English and Chinese README files accordingly * docs(readme): streamline and clarify memory design and operations - Remove redundant explanations about core extra installation - Simplify memory processing flow description for clarity - Clarify memory workspace directory default and customization - Condense automatic memory flow to emphasize rebuildable metadata - Refine search functionality explanation with RRF fusion details - Shorten and clarify agent integration description, removing redundancy - Update and simplify the operations command list, removing less common commands - Revise community and support section for conciseness and clarity - Maintain parallel updates in both English and Chinese README files * test(bump_version): add tests for version bumping and consistency checks - Add dynamic loading of bump_version and package_studio scripts for testing - Test that studio package and dependencies have matching versions - Implement fixtures to write temporary version files for testing - Add test ensuring bump_version updates all relevant files and dependencies - Add test to reject inconsistent version sources before writing - Refactor tests to use common REPOSITORY path variable - Include imports and setup for pytest in test file feat(bump_version): create script to update ReMe and Studio versions - Implement version reading from __init__.py and pyproject.toml files - Validate current versions are consistent across files before updating - Update version strings atomically to avoid partial writes - Ensure exact pinning of studio dependency in main package extras - Validate new version format against a safe pattern - Provide CLI interface to bump versions from command line - Raise errors if expected version declarations or pins are missing or duplicated * fix(release): validate split package publishing * fix(release): improve validation diagnostics * fix(release): sync docs and workflow inputs * fix(release): split PyPI publish jobs
This commit is contained in:
parent
2a05914150
commit
fd2894f939
29 changed files with 822 additions and 226 deletions
61
.github/workflows/github-pages-check.yml
vendored
Normal file
61
.github/workflows/github-pages-check.yml
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
name: GitHub Pages Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, dev, develop]
|
||||
paths:
|
||||
- '.github/workflows/github-pages-check.yml'
|
||||
- 'AGENTS.md'
|
||||
- 'README.md'
|
||||
- 'README_ZH.md'
|
||||
- 'docs/**'
|
||||
- 'github-pages/**'
|
||||
- 'website/README*.md'
|
||||
- 'website/public/og.jpg'
|
||||
- 'cookbook/*/README*.md'
|
||||
- 'benchmark/*/README*.md'
|
||||
- 'skills/reme_memory/SKILL.md'
|
||||
pull_request:
|
||||
branches: [main, master, dev, develop]
|
||||
paths:
|
||||
- '.github/workflows/github-pages-check.yml'
|
||||
- 'AGENTS.md'
|
||||
- 'README.md'
|
||||
- 'README_ZH.md'
|
||||
- 'docs/**'
|
||||
- 'github-pages/**'
|
||||
- 'website/README*.md'
|
||||
- 'website/public/og.jpg'
|
||||
- 'cookbook/*/README*.md'
|
||||
- 'benchmark/*/README*.md'
|
||||
- 'skills/reme_memory/SKILL.md'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-and-build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: github-pages
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '22.13'
|
||||
cache: npm
|
||||
cache-dependency-path: github-pages/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: npm test
|
||||
|
||||
- name: Build documentation
|
||||
run: npm run build
|
||||
97
.github/workflows/package-check.yml
vendored
Normal file
97
.github/workflows/package-check.yml
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
name: Package Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, dev, develop]
|
||||
paths:
|
||||
- '.github/workflows/package-check.yml'
|
||||
- '.github/workflows/python-publish.yml'
|
||||
- 'packages/reme_ai_studio/**'
|
||||
- 'pyproject.toml'
|
||||
- 'reme/__init__.py'
|
||||
- 'reme/utils/web_static.py'
|
||||
- 'scripts/bump_version.py'
|
||||
- 'scripts/package_studio.py'
|
||||
- 'tests/unit/test_package_versions.py'
|
||||
- 'website/**'
|
||||
- 'LICENSE'
|
||||
pull_request:
|
||||
branches: [main, master, dev, develop]
|
||||
paths:
|
||||
- '.github/workflows/package-check.yml'
|
||||
- '.github/workflows/python-publish.yml'
|
||||
- 'packages/reme_ai_studio/**'
|
||||
- 'pyproject.toml'
|
||||
- 'reme/__init__.py'
|
||||
- 'reme/utils/web_static.py'
|
||||
- 'scripts/bump_version.py'
|
||||
- 'scripts/package_studio.py'
|
||||
- 'tests/unit/test_package_versions.py'
|
||||
- 'website/**'
|
||||
- 'LICENSE'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
distributions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.13'
|
||||
cache: npm
|
||||
cache-dependency-path: website/package-lock.json
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install build packaging pytest twine
|
||||
|
||||
- name: Validate release versions
|
||||
run: python scripts/bump_version.py --check
|
||||
|
||||
- name: Run package tests
|
||||
run: PYTHONPATH=. python -m pytest tests/unit/test_package_versions.py -q
|
||||
|
||||
- name: Build Studio static workspace
|
||||
working-directory: website
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:static
|
||||
|
||||
- name: Build and check distributions
|
||||
run: |
|
||||
python scripts/package_studio.py
|
||||
mkdir -p dist/reme dist/studio
|
||||
python -m build --outdir dist/reme
|
||||
python -m build packages/reme_ai_studio --outdir dist/studio
|
||||
python -m twine check dist/reme/* dist/studio/*
|
||||
|
||||
- name: Verify distributions and isolated installation
|
||||
run: |
|
||||
REME_WHEEL="$(pwd)/$(ls dist/reme/reme_ai-[0-9]*.whl)"
|
||||
STUDIO_WHEEL="$(pwd)/$(ls dist/studio/reme_ai_studio-*.whl)"
|
||||
STUDIO_SDIST="$(pwd)/$(ls dist/studio/reme_ai_studio-*.tar.gz)"
|
||||
python -m zipfile -l "${REME_WHEEL}" | (! grep 'reme/web/')
|
||||
python -m zipfile -l "${STUDIO_WHEEL}" | grep 'reme_ai_studio/static/index.html'
|
||||
python -m zipfile -l "${STUDIO_WHEEL}" | grep 'dist-info/licenses/LICENSE'
|
||||
python -m tarfile -l "${STUDIO_SDIST}" | grep '/LICENSE'
|
||||
python -m venv "${RUNNER_TEMP}/reme-package-smoke"
|
||||
"${RUNNER_TEMP}/reme-package-smoke/bin/python" -m pip install \
|
||||
--find-links "$(pwd)/dist/studio" "${REME_WHEEL}[core]"
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/reme-package-smoke/bin/python" -c \
|
||||
"import reme; from reme_ai_studio import static_dir; assert (static_dir() / 'index.html').is_file()"
|
||||
"${RUNNER_TEMP}/reme-package-smoke/bin/python" -c \
|
||||
"from reme.utils import resolve_web_static_dir; assert (resolve_web_static_dir() / 'index.html').is_file()"
|
||||
4
.github/workflows/pages.yml
vendored
4
.github/workflows/pages.yml
vendored
|
|
@ -8,6 +8,10 @@ on:
|
|||
- "docs/**"
|
||||
- "README.md"
|
||||
- "README_ZH.md"
|
||||
- "website/README*.md"
|
||||
- "website/public/og.jpg"
|
||||
- "cookbook/*/README*.md"
|
||||
- "benchmark/*/README*.md"
|
||||
- "skills/reme_memory/SKILL.md"
|
||||
- "AGENTS.md"
|
||||
- ".github/workflows/pages.yml"
|
||||
|
|
|
|||
160
.github/workflows/python-publish.yml
vendored
160
.github/workflows/python-publish.yml
vendored
|
|
@ -1,15 +1,12 @@
|
|||
# This workflow will upload a Python Package using Twine when a release is created
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: Publish Python Package to Pypi
|
||||
name: Publish Python packages to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Release version
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
|
|
@ -17,47 +14,110 @@ permissions:
|
|||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RELEASE_VERSION: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: npm
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Build web workspace
|
||||
working-directory: website
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:static
|
||||
- name: Prepare Studio package
|
||||
run: python scripts/package_studio.py
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel build
|
||||
- name: Build package
|
||||
run: |
|
||||
python -m build --outdir dist
|
||||
python -m build packages/reme_ai_studio --outdir dist
|
||||
- name: Test installation
|
||||
run: |
|
||||
REME_WHEEL="$(pwd)/$(ls dist/reme_ai-[0-9]*.whl)"
|
||||
python -m zipfile -l "${REME_WHEEL}" | (! grep 'reme/web/')
|
||||
pip install --find-links "$(pwd)/dist" "${REME_WHEEL}[core]"
|
||||
python -c "import reme; print(reme.__version__)"
|
||||
python -c "from reme_ai_studio import static_dir; assert (static_dir() / 'index.html').is_file()"
|
||||
python -c "from reme.utils import resolve_web_static_dir; assert (resolve_web_static_dir() / 'index.html').is_file()"
|
||||
- name: Publish package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.13'
|
||||
cache: npm
|
||||
cache-dependency-path: website/package-lock.json
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install build packaging twine
|
||||
|
||||
- name: Validate release version
|
||||
run: python scripts/bump_version.py --check --expected-version "${RELEASE_VERSION}"
|
||||
|
||||
- name: Build Studio static workspace
|
||||
working-directory: website
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:static
|
||||
|
||||
- name: Prepare and build distributions
|
||||
run: |
|
||||
python scripts/package_studio.py
|
||||
mkdir -p dist/reme dist/studio
|
||||
python -m build --outdir dist/reme
|
||||
python -m build packages/reme_ai_studio --outdir dist/studio
|
||||
python -m twine check dist/reme/* dist/studio/*
|
||||
|
||||
- name: Verify distributions and isolated installation
|
||||
run: |
|
||||
REME_WHEEL="$(pwd)/$(ls dist/reme/reme_ai-[0-9]*.whl)"
|
||||
STUDIO_WHEEL="$(pwd)/$(ls dist/studio/reme_ai_studio-*.whl)"
|
||||
STUDIO_SDIST="$(pwd)/$(ls dist/studio/reme_ai_studio-*.tar.gz)"
|
||||
python -m zipfile -l "${REME_WHEEL}" | (! grep 'reme/web/')
|
||||
python -m zipfile -l "${STUDIO_WHEEL}" | grep 'reme_ai_studio/static/index.html'
|
||||
python -m zipfile -l "${STUDIO_WHEEL}" | grep 'dist-info/licenses/LICENSE'
|
||||
python -m tarfile -l "${STUDIO_SDIST}" | grep '/LICENSE'
|
||||
python -m venv "${RUNNER_TEMP}/reme-release-smoke"
|
||||
"${RUNNER_TEMP}/reme-release-smoke/bin/python" -m pip install \
|
||||
--find-links "$(pwd)/dist/studio" "${REME_WHEEL}[core]"
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/reme-release-smoke/bin/python" -c \
|
||||
"import reme; from reme_ai_studio import static_dir; assert (static_dir() / 'index.html').is_file()"
|
||||
"${RUNNER_TEMP}/reme-release-smoke/bin/python" -c \
|
||||
"from reme.utils import resolve_web_static_dir; assert (resolve_web_static_dir() / 'index.html').is_file()"
|
||||
|
||||
- name: Upload ReMe Studio distributions
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: reme-studio-distributions
|
||||
path: dist/studio/
|
||||
|
||||
- name: Upload ReMe distributions
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: reme-distributions
|
||||
path: dist/reme/
|
||||
|
||||
publish-studio:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download ReMe Studio distributions
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: reme-studio-distributions
|
||||
path: dist/studio
|
||||
|
||||
- name: Publish ReMe Studio
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages-dir: dist/studio
|
||||
skip-existing: true
|
||||
|
||||
publish-reme:
|
||||
needs: publish-studio
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download ReMe distributions
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: reme-distributions
|
||||
path: dist/reme
|
||||
|
||||
- name: Publish ReMe
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages-dir: dist/reme
|
||||
skip-existing: true
|
||||
|
|
|
|||
20
.github/workflows/windows-smoke.yml
vendored
20
.github/workflows/windows-smoke.yml
vendored
|
|
@ -23,6 +23,13 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: npm
|
||||
cache-dependency-path: website/package-lock.json
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
|
|
@ -34,6 +41,19 @@ jobs:
|
|||
python -m pip install --upgrade pip setuptools wheel
|
||||
pip install -e packages/reme_ai_studio -e ".[dev,core]"
|
||||
|
||||
- name: Build Studio static workspace
|
||||
working-directory: website
|
||||
run: |
|
||||
npm ci
|
||||
npm run build:static
|
||||
|
||||
- name: Verify editable source installation serves Studio
|
||||
shell: pwsh
|
||||
run: |
|
||||
Push-Location $env:RUNNER_TEMP
|
||||
python -c "from reme.utils import resolve_web_static_dir; assert (resolve_web_static_dir() / 'index.html').is_file()"
|
||||
Pop-Location
|
||||
|
||||
- name: Run version job
|
||||
run: reme start service.backend=cli job=version
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ and concise documentation together.
|
|||
ReMe requires Python 3.11 or newer. Install the editable development environment with:
|
||||
|
||||
```bash
|
||||
pip install -e ".[dev,core]"
|
||||
pip install -e packages/reme_ai_studio -e ".[dev,core]"
|
||||
```
|
||||
|
||||
Before changing behavior, inspect the adjacent implementation, schema, built-in config, and focused tests. Follow
|
||||
|
|
|
|||
98
README.md
98
README.md
|
|
@ -61,10 +61,8 @@ keeping the files under the user's control.
|
|||
|
||||
## 📰 News
|
||||
|
||||
- [2026.08] - Published the [ReMe blog](docs/en/reme-blog.md), an end-to-end introduction to its local-first memory
|
||||
- [2026.08] - Published the [ReMe blog](https://agentscope-ai.github.io/ReMe/?doc=en-reme-blog), an end-to-end introduction to its local-first memory
|
||||
architecture, self-evolving workflows, hybrid search, proactive discovery, and benchmark results.
|
||||
- [2026.08] - Introduced [ReMe Studio](https://reme.agentscope.io/?doc=studio-en), a local web workspace for browsing, editing, and searching
|
||||
memory files, chatting with the read-only ReMe Agent, inspecting the digest wikilink graph, and managing the local service.
|
||||
- [2026.08] - [Experience-driven enhancement method](https://reme.agentscope.io/?doc=toolmemory-en) of agent tool-use execution built
|
||||
on ReMe is available on [arXiv:2608.03403](https://arxiv.org/abs/2608.03403).
|
||||
- [2026.07] - Introduced optional Cookbooks: [Daily Paper](https://reme.agentscope.io/?doc=daily-paper-en) for paper discovery and
|
||||
|
|
@ -86,18 +84,20 @@ Install from pip:
|
|||
pip install "reme-ai[core]"
|
||||
```
|
||||
|
||||
The base `reme-ai` package contains only the Python service and library. ReMe Studio is distributed separately and is
|
||||
installed by the `web` and `core` extras. Use `pip install reme-ai` for embedded or headless integrations that do not
|
||||
need the frontend, or `pip install "reme-ai[web]"` when Studio is needed without the other `core` integrations.
|
||||
|
||||
Install from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/agentscope-ai/ReMe.git
|
||||
cd ReMe
|
||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
||||
cd website
|
||||
npm ci
|
||||
npm run build:static
|
||||
cd ..
|
||||
```
|
||||
|
||||
The static build requires Node.js 22.13 or newer and makes Studio available from the source tree.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Configure environment variables when you want LLM-powered memory evolution or embedding retrieval. Embeddings are
|
||||
|
|
@ -136,14 +136,6 @@ reme start service.port=8181
|
|||
# reme start workspace_dir=/tmp/reme-demo service.port=8181
|
||||
```
|
||||
|
||||
After startup, check the service status. If you use a custom port, replace `2333` in the URL below with that port.
|
||||
|
||||
When the `web` or `core` extra is installed, the HTTP service also serves **ReMe Studio** at
|
||||
<http://127.0.0.1:2333/>. Studio can
|
||||
browse, edit, and search the workspace, chat with the read-only workspace agent, and inspect the digest wikilink graph.
|
||||
Set `service.web_enabled=false` to disable it, or use `service.web_static_dir` / `REME_WEB_STATIC_DIR` to provide a
|
||||
custom static build. The Job API remains available when no web build is found.
|
||||
|
||||
```bash
|
||||
reme version
|
||||
reme health_check
|
||||
|
|
@ -151,32 +143,11 @@ reme help
|
|||
curl -s http://127.0.0.1:2333/version -H 'Content-Type: application/json' -d '{}'
|
||||
```
|
||||
|
||||
### Use ReMe Studio
|
||||
### ReMe Studio (Optional)
|
||||
|
||||
Open <http://127.0.0.1:2333/> after starting the default HTTP service. Studio provides:
|
||||
|
||||
- **Files, Daily, and Knowledge views** for navigating the whole workspace or focusing on `daily/` and `digest/`.
|
||||
- **Markdown tabs** with preview, split editing, optimistic save checks, and local download.
|
||||
- **Memory Graph** for exploring indexed `personal`, `procedure`, and `wiki` nodes and opening their Markdown sources.
|
||||
- **Read-only Agent chat** with streamed tool activity and usage; drag a workspace file into the composer to reference it.
|
||||
- **Settings** for service/component status, redacted effective configuration, version information, and safe index rebuilding.
|
||||
- English/Chinese language switching and light, dark, or system appearance.
|
||||
|
||||
For frontend development, run ReMe and Studio in separate terminals:
|
||||
|
||||
```bash
|
||||
# Terminal 1, repository root
|
||||
reme start
|
||||
|
||||
# Terminal 2
|
||||
cd website
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Then open <http://localhost:3000>. The development server uses `http://127.0.0.1:2333` by default; set
|
||||
`NEXT_PUBLIC_REME_API_URL` to connect to another ReMe HTTP service. Static-build and frontend configuration instructions are
|
||||
in the [ReMe Studio guide](https://reme.agentscope.io/?doc=studio-en).
|
||||
The `core` installation above includes Studio. After starting ReMe, open <http://127.0.0.1:2333/> to browse, edit, and
|
||||
search the workspace. To add Studio to a base installation, use `pip install "reme-ai[web]"`. See the
|
||||
[ReMe Studio guide](https://reme.agentscope.io/?doc=studio-en) for source builds, configuration, and development.
|
||||
|
||||
### 5-Minute Memory Demo
|
||||
|
||||
|
|
@ -218,7 +189,7 @@ These Markdown guides cover the main user workflows and the runtime contracts im
|
|||
|
||||
| Guide | What you will learn |
|
||||
|-------|---------------------|
|
||||
| [Quick Start](docs/en/quick_start.md) | Install ReMe, start the service, use Studio, and run the first file and memory operations. |
|
||||
| [Quick Start](docs/en/quick_start.md) | Install ReMe, start the service, and run the first file and memory operations. |
|
||||
| [Memory as File](docs/en/memory_as_file.md) | Understand workspace layers, frontmatter, wikilinks, chunks, and the file-as-source-of-truth model. |
|
||||
| [Auto Memory](docs/en/auto_memory.md) | Preserve source conversations and distill reusable daily memory cards. |
|
||||
| [Auto Resource](docs/en/auto_resource.md) | Import supported text resources and turn them into source-linked daily cards. |
|
||||
|
|
@ -227,8 +198,7 @@ These Markdown guides cover the main user workflows and the runtime contracts im
|
|||
| [Proactive](docs/en/proactive.md) | Read interest topics safely and integrate them into a host agent's decision flow. |
|
||||
| [Agent Integration Scenarios](docs/en/reme_scene.md) | Choose among CLI/SKILL.md, HTTP, MCP, and embedded Python integration. |
|
||||
| [Framework](docs/en/framework.md) | Understand Application, Job, Step, Component, service, configuration, and lifecycle boundaries. |
|
||||
| [ReMe Studio](https://reme.agentscope.io/?doc=studio-en) | Use, configure, develop, test, and build the web frontend. |
|
||||
| [ReMe Blog](docs/en/reme-blog.md) | Read the product story, design rationale, examples, and benchmark summary. |
|
||||
| [ReMe Blog](https://agentscope-ai.github.io/ReMe/?doc=en-reme-blog) | Read the product story, design rationale, examples, and benchmark summary. |
|
||||
|
||||
## 🧑🍳 Cookbooks
|
||||
|
||||
|
|
@ -246,9 +216,8 @@ another row in this table.
|
|||
> Memory as File, File as Memory.
|
||||
|
||||
ReMe treats **memory as files**, progressively processing filtered conversation source records and external resources
|
||||
from `session/` and `resource/` into `daily/`, then consolidating them into reusable long-term memory nodes under
|
||||
`digest/`. The default workspace is `.reme/` under the current directory; `workspace_dir=...` selects a different
|
||||
user-owned location.
|
||||
from `session/` and `resource/` into `daily/`, then `digest/`. The default workspace is `.reme/` under the current
|
||||
directory; `workspace_dir=...` selects a different user-owned location.
|
||||
|
||||
### Directory Structure
|
||||
|
||||
|
|
@ -288,16 +257,8 @@ user-owned location.
|
|||
|
||||
## 🧭 Memory Design Philosophy
|
||||
|
||||
> Capture conversation source records and resources, refine them into long-term preferences, reusable experience, and
|
||||
> valuable knowledge,
|
||||
> while keeping the result editable by humans and agents.
|
||||
|
||||
### Automatic Memory Flow
|
||||
|
||||
ReMe follows a capture → index → consolidate → recall loop. Conversations and resources first become daily memory cards;
|
||||
background jobs keep files searchable; `auto_dream` distills stable knowledge into `digest/`; agents recall memory
|
||||
through search, wikilinks, or proactive topics. The files are the durable source of truth—indexes, graphs, catalogs, and
|
||||
caches under `metadata/` can be rebuilt from them.
|
||||
ReMe follows a capture → index → consolidate → recall loop. Workspace files remain the durable source of truth;
|
||||
everything under `metadata/` is rebuildable.
|
||||
|
||||
| Capability | Entry point | What it does | Output |
|
||||
|---------------------------------------------|-------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
|
||||
|
|
@ -326,10 +287,8 @@ caches under `metadata/` can be rebuilt from them.
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
Search returns the best matching chunks with file paths and line ranges, then lists bounded incoming and outgoing
|
||||
wikilink neighbors by metadata. An agent can read a promising source or traverse the graph only when needed. With
|
||||
embeddings enabled, BM25 and vector rankings are fused with reciprocal rank fusion (RRF); otherwise the default remains
|
||||
BM25 plus wikilink expansion.
|
||||
Search returns matching chunks with line ranges and bounded wikilink neighbors. Optional vector results are fused with
|
||||
BM25 through reciprocal rank fusion (RRF).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `proactive` only reads and exposes interest topics produced by Auto Dream. It does not independently browse the web,
|
||||
|
|
@ -355,8 +314,7 @@ dependencies, and underspecified requests.
|
|||
## 🤝 Agent-friendly Integration
|
||||
|
||||
ReMe can run as a local memory service accessed through the CLI, HTTP API, or MCP server, or it can be embedded in the
|
||||
host process through its Python API. The default HTTP service can serve ReMe Studio at the same address. Agents can
|
||||
choose the path that fits their runtime and share a local memory workspace when appropriate.
|
||||
host process through its Python API.
|
||||
|
||||
| Agents | Recommended path | Available after integration |
|
||||
|-----------------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
|
|
@ -395,28 +353,21 @@ choose the path that fits their runtime and share a local memory workspace when
|
|||
|
||||
## 🛠️ ReMe Operations
|
||||
|
||||
ReMe operates the workspace through a unified job interface exposed by the CLI. Agents usually only need retrieval,
|
||||
reading, writing, editing, and automatic memory commands. Lower-level indexing, frontmatter, and file operation commands
|
||||
are mainly for maintenance, debugging, or advanced integration. Run `reme help` for the full job list.
|
||||
Run `reme help` for the full job list. Common workspace and maintenance commands are:
|
||||
|
||||
| Command | Purpose |
|
||||
|-------------------------------------------|----------------------------------------------------------------------------------------|
|
||||
| `reme start` | Start the local ReMe service. |
|
||||
| `reme version` / `reme health_check` | Check package and component status. |
|
||||
| `reme status` | Show stateful data-component memory estimates and process RSS. |
|
||||
| [`reme search`](docs/en/memory_search.md) | Retrieve memory with BM25 and wikilinks by default, plus vectors when enabled. |
|
||||
| `reme read` / `reme write` / `reme edit` | Inspect and maintain Markdown memory files. |
|
||||
| `reme traverse` / `reme graph_snapshot` | Explore wikilink neighborhoods or the category-rooted digest graph. |
|
||||
| `reme chat` | Stream a read-only, workspace-aware agent conversation. Requires LLM credentials. |
|
||||
| `reme auto_memory` | Turn conversation messages into daily memory cards. Requires LLM credentials. |
|
||||
| `reme auto_resource` | Interpret files under `resource/` into daily resource cards. Requires LLM credentials. |
|
||||
| `reme auto_dream` / `reme proactive` | Consolidate daily memory into long-term digest and surface topics worth attention. |
|
||||
| `reme reindex` | Rebuild search and wikilink indexes from existing files. |
|
||||
|
||||
## 🤝 Community and Support
|
||||
|
||||
- **Issues and requests**: Check [Open Issues](https://github.com/agentscope-ai/ReMe/issues) first. If there is no
|
||||
related discussion, open a new issue with background, expected behavior, and impact scope.
|
||||
- **Issues, requests, and help**: Check [Open Issues](https://github.com/agentscope-ai/ReMe/issues) first. If there is no
|
||||
related discussion, open one with the background, expected behavior, and impact scope.
|
||||
- **Code contributions**: Before making changes, read
|
||||
the [contribution guide](https://docs.agentscope.io/reme/latest/en/contribution). Source, schemas, and tests are the
|
||||
authoritative architecture and extension guide.
|
||||
|
|
@ -426,8 +377,7 @@ are mainly for maintenance, debugging, or advanced integration. Run `reme help`
|
|||
`docs(zh): update quick start`.
|
||||
- **Pre-submit checks**: Before submitting a PR, try to run `pre-commit run --all-files` and `pytest`. If tests that
|
||||
depend on LLMs, embeddings, or external services cannot run, explain that in the PR.
|
||||
- **Get help**: Use [GitHub Issues](https://github.com/agentscope-ai/ReMe/issues) for bugs and feature requests. Project
|
||||
documentation is available at [https://reme.agentscope.io](https://reme.agentscope.io).
|
||||
- **Documentation**: Visit [reme.agentscope.io](https://reme.agentscope.io).
|
||||
|
||||
### Contributors
|
||||
|
||||
|
|
|
|||
82
README_ZH.md
82
README_ZH.md
|
|
@ -53,10 +53,8 @@ Code 等 Agent 协作,在持续整理知识的同时,始终把文件控制
|
|||
|
||||
## 📰 新闻
|
||||
|
||||
- [2026.08] - 发布 [ReMe 博客](docs/zh/reme-blog.md),系统介绍本地优先的记忆架构、自进化工作流、混合检索、
|
||||
- [2026.08] - 发布 [ReMe 博客](https://agentscope-ai.github.io/ReMe/?doc=zh-reme-blog),系统介绍本地优先的记忆架构、自进化工作流、混合检索、
|
||||
主动发现与评测结果。
|
||||
- [2026.08] - 新增 [ReMe Studio](https://reme.agentscope.io/?doc=studio-zh):用于浏览、编辑和搜索记忆文件,与只读 ReMe Agent
|
||||
对话,查看 digest wikilink 图,并管理本地服务。
|
||||
- [2026.08] - 基于 ReMe 的智能体工具使用
|
||||
[经验驱动增强方法](https://reme.agentscope.io/?doc=toolmemory-zh)已发布,见
|
||||
[arXiv:2608.03403](https://arxiv.org/abs/2608.03403)。
|
||||
|
|
@ -84,9 +82,15 @@ pip install "reme-ai[core]"
|
|||
```bash
|
||||
git clone https://github.com/agentscope-ai/ReMe.git
|
||||
cd ReMe
|
||||
pip install -e ".[core]"
|
||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
||||
cd website
|
||||
npm ci
|
||||
npm run build:static
|
||||
cd ..
|
||||
```
|
||||
|
||||
静态构建要求 Node.js 22.13 或更高版本,并让源码安装可以直接使用 Studio。
|
||||
|
||||
### 环境变量
|
||||
|
||||
如果需要 LLM 驱动的记忆演化或 embedding 检索,可以配置环境变量。embedding 默认关闭,因此默认配置不会启动 embedding 模型,也不需要
|
||||
|
|
@ -125,13 +129,6 @@ reme start service.port=8181
|
|||
# reme start workspace_dir=/tmp/reme-demo service.port=8181
|
||||
```
|
||||
|
||||
启动后可以检查服务状态;如果使用了自定义端口,请将下面 URL 中的 `2333` 替换为对应端口。
|
||||
|
||||
如果安装包中包含 Web 构建产物,HTTP 服务还会在 <http://127.0.0.1:2333/> 提供 **ReMe Studio**,用于浏览、编辑和搜索
|
||||
workspace,与只读 workspace agent 对话,以及查看 digest wikilink 图。可以设置
|
||||
`service.web_enabled=false` 关闭,或通过 `service.web_static_dir` / `REME_WEB_STATIC_DIR` 指定自定义静态目录。找不到 Web
|
||||
构建产物时,Job API 仍可正常使用。
|
||||
|
||||
```bash
|
||||
reme version
|
||||
reme health_check
|
||||
|
|
@ -139,31 +136,11 @@ reme help
|
|||
curl -s http://127.0.0.1:2333/version -H 'Content-Type: application/json' -d '{}'
|
||||
```
|
||||
|
||||
### 使用 ReMe Studio
|
||||
### ReMe Studio(可选)
|
||||
|
||||
启动默认 HTTP 服务后,在浏览器打开 <http://127.0.0.1:2333/>。Studio 提供:
|
||||
|
||||
- **文件、日记和知识库视图**:浏览整个 workspace,或聚焦 `daily/` 和 `digest/`。
|
||||
- **Markdown 多标签页**:支持预览、分栏编辑、基于修改时间的冲突检查、保存和本地下载。
|
||||
- **记忆图谱**:浏览已索引的 `personal`、`procedure` 和 `wiki` 节点,并打开对应 Markdown 源文件。
|
||||
- **只读 Agent 对话**:流式查看工具调用和模型用量;可将 workspace 文件拖入输入框作为引用。
|
||||
- **设置与服务管理**:查看服务/组件状态、脱敏后的生效配置和版本,并安全重建派生索引。
|
||||
- 支持中英文切换,以及浅色、深色和跟随系统外观。
|
||||
|
||||
如需开发前端,在两个终端中分别启动 ReMe 和 Studio:
|
||||
|
||||
```bash
|
||||
# 终端 1:仓库根目录
|
||||
reme start
|
||||
|
||||
# 终端 2
|
||||
cd website
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
然后打开 <http://localhost:3000>。开发服务默认连接 `http://127.0.0.1:2333`;如需连接其他 ReMe HTTP
|
||||
服务,请设置 `NEXT_PUBLIC_REME_API_URL`。静态构建和前端配置说明见 [ReMe Studio 指南](https://reme.agentscope.io/?doc=studio-zh)。
|
||||
上面的 `core` 安装已包含 Studio。启动 ReMe 后,打开 <http://127.0.0.1:2333/> 即可浏览、编辑和搜索 workspace。
|
||||
如需为基础安装单独添加 Studio,可使用 `pip install "reme-ai[web]"`。源码构建、配置和开发说明见
|
||||
[ReMe Studio 指南](https://reme.agentscope.io/?doc=studio-zh)。
|
||||
|
||||
### 5 分钟记忆 Demo
|
||||
|
||||
|
|
@ -205,7 +182,7 @@ ReMe 会把 Agent 记忆保存为可读的 Markdown。
|
|||
|
||||
| 文档 | 主要内容 |
|
||||
|------|----------|
|
||||
| [快速开始](docs/zh/quick_start.md) | 安装 ReMe、启动服务、使用 Studio,并执行首次文件和记忆操作。 |
|
||||
| [快速开始](docs/zh/quick_start.md) | 安装 ReMe、启动服务,并执行首次文件和记忆操作。 |
|
||||
| [Memory as File](docs/zh/memory_as_file.md) | 理解 workspace 分层、frontmatter、wikilink、chunk 和文件事实来源模型。 |
|
||||
| [Auto Memory](docs/zh/auto_memory.md) | 保留过滤后的对话来源记录,并提炼可复用的 daily 记忆卡片。 |
|
||||
| [Auto Resource](docs/zh/auto_resource.md) | 导入支持的文本资料,转换为可追溯来源的 daily 卡片。 |
|
||||
|
|
@ -214,8 +191,7 @@ ReMe 会把 Agent 记忆保存为可读的 Markdown。
|
|||
| [Proactive](docs/zh/proactive.md) | 安全读取兴趣主题,并将其接入宿主 Agent 的决策流程。 |
|
||||
| [Agent 接入场景](docs/zh/reme_scene.md) | 在 CLI/SKILL.md、HTTP、MCP 和嵌入式 Python 集成之间选择。 |
|
||||
| [框架说明](docs/zh/framework.md) | 理解 Application、Job、Step、Component、service、配置和生命周期边界。 |
|
||||
| [ReMe Studio](https://reme.agentscope.io/?doc=studio-zh) | 使用、配置、开发、测试和构建 Web 前端。 |
|
||||
| [ReMe 博客](docs/zh/reme-blog.md) | 了解完整产品故事、设计动机、使用示例和评测摘要。 |
|
||||
| [ReMe 博客](https://agentscope-ai.github.io/ReMe/?doc=zh-reme-blog) | 了解完整产品故事、设计动机、使用示例和评测摘要。 |
|
||||
|
||||
## 🧑🍳 Cookbooks
|
||||
|
||||
|
|
@ -231,8 +207,8 @@ cookbook 会继续在表格中按行追加。
|
|||
|
||||
> Memory as File, File as Memory.
|
||||
|
||||
ReMe 将 **记忆视为文件**,让过滤后的对话来源记录和外部资料从 `session/`、`resource/` 渐进加工到 `daily/`,再沉淀为 `digest/`
|
||||
中可长期复用的知识节点。默认 workspace 是当前目录下的 `.reme/`;可通过 `workspace_dir=...` 选择其他由用户控制的位置。
|
||||
ReMe 将 **记忆视为文件**,让过滤后的对话来源记录和外部资料从 `session/`、`resource/` 渐进加工到 `daily/`,再沉淀为
|
||||
`digest/`。默认 workspace 是当前目录下的 `.reme/`;可通过 `workspace_dir=...` 选择其他由用户控制的位置。
|
||||
|
||||
### 目录结构
|
||||
|
||||
|
|
@ -272,13 +248,7 @@ ReMe 将 **记忆视为文件**,让过滤后的对话来源记录和外部资
|
|||
|
||||
## 🧭 记忆设计理念
|
||||
|
||||
> 捕获过滤后的对话来源记录和资料,将其整理为长期偏好、可复用经验和有价值的知识,并让结果始终能被用户和 Agent 直接编辑。
|
||||
|
||||
### 自动记忆流程
|
||||
|
||||
ReMe 遵循 capture → index → consolidate → recall 的循环。对话和资料先变成 daily 记忆卡片;后台任务保持文件可检索;
|
||||
`auto_dream` 将稳定知识沉淀到 `digest/`;Agent 再通过搜索、wikilink 或 proactive topics 召回记忆。文件是持久化的事实来源,
|
||||
`metadata/` 中的索引、图谱、catalog 和缓存都可以由它们重建。
|
||||
ReMe 遵循 capture → index → consolidate → recall 的循环。workspace 文件是持久化的事实来源,`metadata/` 中的内容均可重建。
|
||||
|
||||
| 能力 | 入口 | 作用 | 输出 |
|
||||
|---------------------------------------------|-------------------------------------------|----------------------------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
|
|
@ -307,8 +277,7 @@ ReMe 遵循 capture → index → consolidate → recall 的循环。对话和
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
搜索会先返回最相关的 chunks、文件路径和行号范围,再以元数据形式列出数量受限的入链与出链邻居。Agent 只需在判断确实相关后再读取原文或继续遍历图谱。
|
||||
启用 embedding 时,BM25 和向量排名通过 RRF 融合;默认未启用 embedding 时,则使用 BM25 + wikilink 扩展。
|
||||
搜索返回带行号范围的相关 chunks 和数量受限的 wikilink 邻居;可选向量结果通过 RRF 与 BM25 融合。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `proactive` 只读取并暴露 Auto Dream 生成的兴趣主题,不会自行联网、发送通知或改写知识库;是否以及如何使用主题,由宿主 Agent
|
||||
|
|
@ -330,7 +299,7 @@ ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上
|
|||
## 🤝 Agent-friendly Integration
|
||||
|
||||
ReMe 既可以作为本地记忆服务,通过 CLI、HTTP API 或 MCP server 接入,也可以通过 Python API 嵌入宿主进程。不同 Agent 可以选择适合自身
|
||||
runtime 的路径,并按需共享同一个本地 memory workspace。默认 HTTP 服务还可以在同一地址提供 ReMe Studio。
|
||||
runtime 的路径。
|
||||
|
||||
| Agent | 推荐接入方式 | 接入后能力 |
|
||||
|-----------------------------------------------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
|
||||
|
|
@ -369,26 +338,20 @@ runtime 的路径,并按需共享同一个本地 memory workspace。默认 HTT
|
|||
|
||||
## 🛠️ ReMe Operations
|
||||
|
||||
ReMe 通过 CLI 暴露的统一 job interface 操作 workspace。Agent 通常只需要使用检索、读取、写入、编辑和自动记忆相关命令;更底层的索引、
|
||||
frontmatter 和文件操作接口主要用于维护、调试或高级集成。完整 job 列表可以运行 `reme help` 查看。
|
||||
运行 `reme help` 可查看完整 job 列表。常用 workspace 与维护命令如下:
|
||||
|
||||
| 命令 | 作用 |
|
||||
|-------------------------------------------|---------------------------------------------------------------|
|
||||
| `reme start` | 启动本地 ReMe 服务。 |
|
||||
| `reme version` / `reme health_check` | 检查包版本和组件状态。 |
|
||||
| `reme status` | 查看有状态数据组件的内存估算及进程 RSS。 |
|
||||
| [`reme search`](docs/zh/memory_search.md) | 默认使用 BM25 和 wikilink 检索,启用后增加向量检索。 |
|
||||
| `reme read` / `reme write` / `reme edit` | 检查和维护 Markdown 记忆文件。 |
|
||||
| `reme traverse` / `reme graph_snapshot` | 浏览 wikilink 邻域或按类别组织的 digest 图。 |
|
||||
| `reme chat` | 与可感知 workspace 的只读 Agent 进行流式对话;需要 LLM 凭证。 |
|
||||
| `reme auto_memory` | 将对话 messages 转为 daily 记忆卡片;需要 LLM 凭证。 |
|
||||
| `reme auto_resource` | 将 `resource/` 下的文件解读为 daily 资料卡片;需要 LLM 凭证。 |
|
||||
| `reme auto_dream` / `reme proactive` | 将 daily 记忆整理为长期 digest,并暴露值得关注的主题。 |
|
||||
| `reme reindex` | 基于已有文件重建检索和 wikilink 索引。 |
|
||||
|
||||
## 🤝 社区与支持
|
||||
|
||||
- **问题反馈与需求**:请先查看 [Open Issues](https://github.com/agentscope-ai/ReMe/issues);如无相关讨论,可新建 Issue
|
||||
- **问题反馈、需求与帮助**:请先查看 [Open Issues](https://github.com/agentscope-ai/ReMe/issues);如无相关讨论,可新建 Issue
|
||||
说明背景、目标行为和影响范围。
|
||||
- **代码贡献**:改动前建议阅读 [贡献指南](https://docs.agentscope.io/reme/latest/zh/contribution)。架构与扩展方式以源码、schema
|
||||
和测试为准。
|
||||
|
|
@ -397,8 +360,7 @@ frontmatter 和文件操作接口主要用于维护、调试或高级集成。
|
|||
`docs(zh): update quick start`。
|
||||
- **提交前检查**:提交 PR 前请尽量运行 `pre-commit run --all-files` 和 `pytest`;如有依赖 LLM、embedding 或外部服务的测试无法运行,请在
|
||||
PR 中说明。
|
||||
- **获取帮助**:如需反馈 Bug 或功能请求,请使用 [GitHub Issues](https://github.com/agentscope-ai/ReMe/issues);项目文档见
|
||||
[https://reme.agentscope.io](https://reme.agentscope.io)。
|
||||
- **项目文档**:访问 [reme.agentscope.io](https://reme.agentscope.io)。
|
||||
|
||||
### 贡献者
|
||||
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ when present. The two data sources reach a mirror differently: Hugging Face is g
|
|||
parameter, while arXiv is driven by its environment variable alone.
|
||||
|
||||
```dotenv
|
||||
# Enable the mirror for the built-in daily_paper_cron job
|
||||
DAILY_PAPER_USE_HF_MIRROR=true
|
||||
# The built-in daily_paper_cron job enables the mirror by default; set false to use the official service
|
||||
DAILY_PAPER_USE_HF_MIRROR=false
|
||||
|
||||
# Read only when the manual or scheduled job enables the mirror; defaults to https://hf-mirror.com when unset
|
||||
HF_MIRROR_URL=https://hf-mirror.com
|
||||
|
|
@ -202,8 +202,8 @@ trailing slash is optional. There is no fallback chain: whichever base URL a cli
|
|||
|
||||
> **Behavior change:** `HF_MIRROR_URL` used to redirect Hugging Face traffic on its own. It is now read only when the
|
||||
> job runs with `use_hf_mirror=true`; otherwise the official service is used and the client logs a warning that the
|
||||
> variable was ignored. Pass `use_hf_mirror=true` for manual requests, or set
|
||||
> `DAILY_PAPER_USE_HF_MIRROR=true` for `daily_paper_cron`, to keep an existing mirror-only setup working.
|
||||
> variable was ignored. Pass `use_hf_mirror=true` for manual requests. The built-in `daily_paper_cron` job enables the
|
||||
> mirror by default; set `DAILY_PAPER_USE_HF_MIRROR=false` to make that scheduled job use the official service.
|
||||
|
||||
## Running the workflow
|
||||
|
||||
|
|
@ -231,8 +231,9 @@ reme start config=daily_cookbook
|
|||
```
|
||||
|
||||
The built-in service listens on `127.0.0.1:8001`. `daily_paper_cron` runs every day at 08:00 in the
|
||||
`Asia/Shanghai` timezone. Set `DAILY_PAPER_USE_HF_MIRROR=true` to make that scheduled job use the Hugging Face mirror.
|
||||
Override the bind address with `DAILY_PAPER_HOST`, `DAILY_PAPER_PORT`, or startup arguments.
|
||||
`Asia/Shanghai` timezone, prioritizes the topic `大模型长期记忆`, and uses the Hugging Face mirror by default. Set
|
||||
`DAILY_PAPER_USE_HF_MIRROR=false` to use the official service. Override the bind address with `DAILY_PAPER_HOST`,
|
||||
`DAILY_PAPER_PORT`, or startup arguments.
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8001/daily_paper \
|
||||
|
|
|
|||
|
|
@ -172,8 +172,8 @@ reme_workspace/
|
|||
Face 由 `use_hf_mirror` 任务参数控制,arXiv 仅由环境变量驱动。
|
||||
|
||||
```dotenv
|
||||
# 为内置 daily_paper_cron 定时任务启用镜像站
|
||||
DAILY_PAPER_USE_HF_MIRROR=true
|
||||
# 内置 daily_paper_cron 定时任务默认启用镜像站;设为 false 可改用官方服务
|
||||
DAILY_PAPER_USE_HF_MIRROR=false
|
||||
|
||||
# 仅在手动任务或定时任务启用镜像时读取;未配置时使用 https://hf-mirror.com
|
||||
HF_MIRROR_URL=https://hf-mirror.com
|
||||
|
|
@ -192,7 +192,8 @@ URL,就只访问该地址。
|
|||
|
||||
> **行为变更:** 以往只要设置 `HF_MIRROR_URL` 就会改变 Hugging Face
|
||||
> 的访问地址;现在该变量仅在任务启用镜像时才会读取,否则直接访问官方站点,并输出一条“已忽略该变量”的告警日志。手动调用需传入
|
||||
> `use_hf_mirror=true`,`daily_paper_cron` 定时任务需设置 `DAILY_PAPER_USE_HF_MIRROR=true`,才能继续走镜像。
|
||||
> `use_hf_mirror=true`。内置 `daily_paper_cron` 定时任务默认启用镜像;设置 `DAILY_PAPER_USE_HF_MIRROR=false`
|
||||
> 可让该定时任务改用官方服务。
|
||||
|
||||
## 运行方式
|
||||
|
||||
|
|
@ -219,9 +220,9 @@ reme start config=daily_cookbook job=daily_paper date=2026-08-06 force=true
|
|||
reme start config=daily_cookbook
|
||||
```
|
||||
|
||||
内置服务监听 `127.0.0.1:8001`,`daily_paper_cron` 按 `Asia/Shanghai` 时区每天 08:00 运行。设置
|
||||
`DAILY_PAPER_USE_HF_MIRROR=true` 可让该定时任务使用 Hugging Face 镜像站。可通过 `DAILY_PAPER_HOST`、`DAILY_PAPER_PORT`
|
||||
或启动参数覆盖监听地址和端口。
|
||||
内置服务监听 `127.0.0.1:8001`,`daily_paper_cron` 按 `Asia/Shanghai` 时区每天 08:00 运行,默认优先关注
|
||||
`大模型长期记忆`,并使用 Hugging Face 镜像站。设置 `DAILY_PAPER_USE_HF_MIRROR=false` 可改用官方服务。可通过
|
||||
`DAILY_PAPER_HOST`、`DAILY_PAPER_PORT` 或启动参数覆盖监听地址和端口。
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8001/daily_paper \
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ The project requires Python 3.11 or later. A virtual environment is recommended:
|
|||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e packages/reme_ai_studio -e ".[dev,full]"
|
||||
cd website
|
||||
npm ci
|
||||
npm run build:static
|
||||
cd ..
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@ Install from source:
|
|||
git clone https://github.com/agentscope-ai/ReMe.git
|
||||
cd ReMe
|
||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
||||
cd website
|
||||
npm ci
|
||||
npm run build:static
|
||||
cd ..
|
||||
```
|
||||
|
||||
The static build step requires Node.js 22.13 or newer and makes Studio available when running ReMe from the source tree.
|
||||
|
||||
Installing the `core` extra is recommended. The current code imports the AgentScope wrapper, and self-evolving memory
|
||||
also depends on it.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ ReMe 的核心代码位于:
|
|||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e packages/reme_ai_studio -e ".[dev,full]"
|
||||
cd website
|
||||
npm ci
|
||||
npm run build:static
|
||||
cd ..
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@ pip install "reme-ai[core]"
|
|||
git clone https://github.com/agentscope-ai/ReMe.git
|
||||
cd ReMe
|
||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
||||
cd website
|
||||
npm ci
|
||||
npm run build:static
|
||||
cd ..
|
||||
```
|
||||
|
||||
静态构建步骤需要 Node.js 22.13 或更高版本,用于在从源码运行 ReMe 时提供 Studio。
|
||||
|
||||
`core` extra 建议安装:当前代码会导入 AgentScope wrapper,自进化记忆也依赖它。
|
||||
|
||||
如果要使用 `auto_memory`、`auto_resource`、`auto_dream` 这类 Agent 流程,再配置 LLM:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
"scripts": {
|
||||
"dev": "node scripts/generate-content.mjs && vite",
|
||||
"build": "node scripts/generate-content.mjs && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"test": "node --test tests/*.test.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"dompurify": "^3.2.6",
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ for (const product of productDocuments) {
|
|||
}
|
||||
}
|
||||
await mkdir(path.join(outputDir, "website", "public"), { recursive: true });
|
||||
await cp(path.join(repoDir, "website", "public", "og.png"), path.join(outputDir, "website", "public", "og.png"));
|
||||
await cp(path.join(repoDir, "website", "public", "og.jpg"), path.join(outputDir, "website", "public", "og.jpg"));
|
||||
await mkdir(path.join(outputDir, "skills", "reme_memory"), { recursive: true });
|
||||
await cp(
|
||||
path.join(repoDir, "skills", "reme_memory", "SKILL.md"),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import DOMPurify from "dompurify";
|
||||
import { marked } from "marked";
|
||||
import { stripMarkdownFrontmatter } from "./markdown.js";
|
||||
import "./styles.css";
|
||||
|
||||
const baseUrl = import.meta.env.BASE_URL;
|
||||
|
|
@ -370,7 +371,7 @@ async function openDocument(id, pushHistory = true) {
|
|||
const response = await fetch(`${baseUrl}content/${document.path}`);
|
||||
if (!response.ok) throw new Error(`Unable to load ${document.path}`);
|
||||
configureMarkdown(document);
|
||||
const markdown = await response.text();
|
||||
const markdown = stripMarkdownFrontmatter(await response.text());
|
||||
const body = DOMPurify.sanitize(await marked.parse(markdown), {
|
||||
ADD_ATTR: ["target"],
|
||||
});
|
||||
|
|
|
|||
6
github-pages/src/markdown.js
Normal file
6
github-pages/src/markdown.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const FRONTMATTER_PATTERN = /^\uFEFF?---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/;
|
||||
|
||||
/** Remove a leading YAML frontmatter block before rendering Markdown. */
|
||||
export function stripMarkdownFrontmatter(markdown) {
|
||||
return markdown.replace(FRONTMATTER_PATTERN, "");
|
||||
}
|
||||
|
|
@ -91,8 +91,8 @@ a { color: inherit; }
|
|||
.article[data-group="home"] { max-width: 980px; }
|
||||
.home-hero { padding: 34px 0 76px; }
|
||||
.home-eyebrow, .section-kicker { margin: 0 0 17px; color: #12806d; font: 750 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.13em; }
|
||||
.home-hero h1 { max-width: 800px; margin: 0; color: #102019; font-size: clamp(46px, 6.3vw, 76px); line-height: 0.99; letter-spacing: -0.055em; }
|
||||
.home-lead { max-width: 720px; margin: 27px 0 0; color: #526159; font-size: 18px; line-height: 1.72; }
|
||||
.home-hero h1 { margin: 0; color: #102019; font-size: clamp(46px, 6.3vw, 76px); line-height: 1.15; letter-spacing: -0.055em; }
|
||||
.home-lead { margin: 27px 0 0; color: #526159; font-size: 18px; line-height: 1.72; }
|
||||
.home-actions { display: flex; flex-wrap: wrap; gap: 11px; margin-top: 31px; }
|
||||
.home-actions a { padding: 11px 17px; border-radius: 10px; text-decoration: none; font-size: 14px; font-weight: 720; }
|
||||
.primary-action { color: white; background: #087f6a; box-shadow: 0 8px 22px rgba(8, 127, 106, 0.2); }
|
||||
|
|
|
|||
15
github-pages/tests/markdown.test.mjs
Normal file
15
github-pages/tests/markdown.test.mjs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { stripMarkdownFrontmatter } from "../src/markdown.js";
|
||||
|
||||
test("strips leading YAML frontmatter before rendering", () => {
|
||||
assert.equal(
|
||||
stripMarkdownFrontmatter("---\nname: reme_memory\ndescription: Memory skill\n---\n\n# ReMe Memory\n"),
|
||||
"\n# ReMe Memory\n",
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves Markdown without frontmatter", () => {
|
||||
const markdown = "# ReMe Memory\n\nContent\n";
|
||||
assert.equal(stripMarkdownFrontmatter(markdown), markdown);
|
||||
});
|
||||
1
packages/reme_ai_studio/.gitignore
vendored
Normal file
1
packages/reme_ai_studio/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/LICENSE
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
[project]
|
||||
name = "reme-ai-studio"
|
||||
version = "0.4.1.6"
|
||||
version = "0.4.1.7"
|
||||
description = "Optional ReMe Studio static frontend."
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE"]
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[project.urls]
|
||||
|
|
@ -20,5 +21,5 @@ include-package-data = false
|
|||
"reme_ai_studio" = ["static/**"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "wheel"]
|
||||
requires = ["setuptools>=77", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ dependencies = [
|
|||
|
||||
[project.optional-dependencies]
|
||||
web = [
|
||||
"reme-ai-studio==0.4.1.6",
|
||||
"reme-ai-studio==0.4.1.7",
|
||||
]
|
||||
core = [
|
||||
"agentscope==2.0.4.post1",
|
||||
|
|
@ -58,9 +58,10 @@ core = [
|
|||
"neo4j>=6.2.0",
|
||||
"networkx>=3.4.2",
|
||||
"polars>=1.43.0",
|
||||
"reme-ai-studio==0.4.1.6",
|
||||
"reme-ai-studio==0.4.1.7",
|
||||
]
|
||||
dev = [
|
||||
"packaging>=24.2",
|
||||
"pre-commit>=4.6.1",
|
||||
"pytest>=9.1.1",
|
||||
"pytest-asyncio>=0.23",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""ReMe CLI package."""
|
||||
|
||||
__version__ = "0.4.1.6"
|
||||
__version__ = "0.4.1.7"
|
||||
|
||||
from . import config
|
||||
from . import constants
|
||||
|
|
|
|||
|
|
@ -441,7 +441,8 @@ jobs:
|
|||
daily_paper_cron:
|
||||
backend: cron
|
||||
cron: "0 8 * * *"
|
||||
use_hf_mirror: ${DAILY_PAPER_USE_HF_MIRROR:-false}
|
||||
topics: "大模型长期记忆"
|
||||
use_hf_mirror: ${DAILY_PAPER_USE_HF_MIRROR:-true}
|
||||
candidate_limit: *candidate_limit
|
||||
rrf_k: *rrf_k
|
||||
weekly_weight: *weekly_weight
|
||||
|
|
|
|||
205
scripts/bump_version.py
Normal file
205
scripts/bump_version.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
"""Validate or update the ReMe and ReMe Studio release version."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
from packaging.version import InvalidVersion, Version
|
||||
|
||||
REPOSITORY_DIR = Path(__file__).resolve().parents[1]
|
||||
|
||||
_VERSION_PATTERN = re.compile(r'(?m)^__version__ = "(?P<version>[^"]+)"$')
|
||||
_STUDIO_VERSION_PATTERN = re.compile(r'(?m)^version = "(?P<version>[^"]+)"$')
|
||||
|
||||
|
||||
def _paths(repository: Path) -> tuple[Path, Path, Path]:
|
||||
return (
|
||||
repository / "reme" / "__init__.py",
|
||||
repository / "pyproject.toml",
|
||||
repository / "packages" / "reme_ai_studio" / "pyproject.toml",
|
||||
)
|
||||
|
||||
|
||||
def _canonical_version(value: str) -> str:
|
||||
"""Return one canonical PEP 440 version or explain how to correct it."""
|
||||
try:
|
||||
canonical = str(Version(value))
|
||||
except InvalidVersion as error:
|
||||
raise ValueError(
|
||||
f"Invalid PEP 440 version {value!r}. Use a version such as 0.4.1.8, then rerun this command.",
|
||||
) from error
|
||||
if canonical != value:
|
||||
raise ValueError(f"Version {value!r} is not canonical PEP 440; use {canonical!r} instead.")
|
||||
return canonical
|
||||
|
||||
|
||||
def _read_toml(source: Path) -> dict:
|
||||
"""Read one TOML file or identify the file that needs correction."""
|
||||
try:
|
||||
return tomllib.loads(source.read_text(encoding="utf-8"))
|
||||
except (OSError, UnicodeError, tomllib.TOMLDecodeError) as error:
|
||||
raise ValueError(f"{source}: cannot read valid TOML ({error}). Fix this file and retry.") from error
|
||||
|
||||
|
||||
def _required_table(config: dict, keys: tuple[str, ...], source: Path) -> dict:
|
||||
"""Return a required TOML table or explain where to add it."""
|
||||
value: object = config
|
||||
for key in keys:
|
||||
if not isinstance(value, dict) or key not in value:
|
||||
table = ".".join(keys)
|
||||
raise ValueError(f"{source}: missing or invalid [{table}] table. Add or fix this table and retry.")
|
||||
value = value[key]
|
||||
if not isinstance(value, dict):
|
||||
table = ".".join(keys)
|
||||
raise ValueError(f"{source}: [{table}] must be a TOML table. Fix this table and retry.")
|
||||
return value
|
||||
|
||||
|
||||
def _match_version(text: str, pattern: re.Pattern[str], source: Path) -> str:
|
||||
matches = list(pattern.finditer(text))
|
||||
if len(matches) != 1:
|
||||
raise ValueError(f"Expected exactly one version declaration in {source}, found {len(matches)}.")
|
||||
return matches[0].group("version")
|
||||
|
||||
|
||||
def _replace_version(text: str, pattern: re.Pattern[str], version: str, source: Path) -> str:
|
||||
_match_version(text, pattern, source)
|
||||
return pattern.sub(lambda match: match.group(0).replace(match.group("version"), version), text)
|
||||
|
||||
|
||||
def _write_atomic(path: Path, content: str) -> None:
|
||||
"""Replace one text file without exposing a partially written file."""
|
||||
temporary_name: str | None = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=path.parent, delete=False) as temporary:
|
||||
temporary.write(content)
|
||||
temporary_name = temporary.name
|
||||
os.chmod(temporary_name, path.stat().st_mode)
|
||||
os.replace(temporary_name, path)
|
||||
finally:
|
||||
if temporary_name is not None and os.path.exists(temporary_name):
|
||||
os.unlink(temporary_name)
|
||||
|
||||
|
||||
def read_version(repository: Path = REPOSITORY_DIR) -> str:
|
||||
"""Read and validate the main distribution version."""
|
||||
version_file, _, _ = _paths(repository)
|
||||
version = _match_version(version_file.read_text(encoding="utf-8"), _VERSION_PATTERN, version_file)
|
||||
try:
|
||||
return _canonical_version(version)
|
||||
except ValueError as error:
|
||||
raise ValueError(f"{version_file}: {error} Fix the __version__ declaration and retry.") from error
|
||||
|
||||
|
||||
def check_versions(repository: Path = REPOSITORY_DIR, expected_version: str | None = None) -> str:
|
||||
"""Validate both distributions and their exact optional-dependency pins."""
|
||||
version_file, main_config_file, studio_config_file = _paths(repository)
|
||||
version = read_version(repository)
|
||||
main_config = _read_toml(main_config_file)
|
||||
studio_config = _read_toml(studio_config_file)
|
||||
dependency = f"reme-ai-studio=={version}"
|
||||
optional_dependencies = _required_table(main_config, ("project", "optional-dependencies"), main_config_file)
|
||||
studio_project = _required_table(studio_config, ("project",), studio_config_file)
|
||||
problems: list[str] = []
|
||||
|
||||
studio_version = studio_project.get("version")
|
||||
if studio_version != version:
|
||||
problems.append(f"{studio_config_file}: project.version is {studio_version!r}; expected {version!r}")
|
||||
if optional_dependencies.get("web") != [dependency]:
|
||||
problems.append(f"{main_config_file}: web must be exactly [{dependency!r}]")
|
||||
core_dependencies = optional_dependencies.get("core")
|
||||
if not isinstance(core_dependencies, list) or core_dependencies.count(dependency) != 1:
|
||||
problems.append(f"{main_config_file}: core must contain {dependency!r} exactly once")
|
||||
|
||||
expected = None
|
||||
if expected_version is not None:
|
||||
expected = _canonical_version(expected_version.removeprefix("v"))
|
||||
if version != expected:
|
||||
problems.append(f"{version_file}: package version is {version!r}; release expects {expected!r}")
|
||||
|
||||
if problems:
|
||||
details = "\n".join(f"- {problem}" for problem in problems)
|
||||
raise ValueError(
|
||||
"Version metadata is inconsistent:\n"
|
||||
f"{details}\n"
|
||||
f"Fix the listed values, or run `python scripts/bump_version.py {expected or version}` "
|
||||
"after restoring a consistent current version.",
|
||||
)
|
||||
return version
|
||||
|
||||
|
||||
def bump_version(version: str, repository: Path = REPOSITORY_DIR) -> str:
|
||||
"""Update every release version, rolling back if any write or validation fails."""
|
||||
version = _canonical_version(version)
|
||||
current_version = check_versions(repository)
|
||||
version_file, main_config_file, studio_config_file = _paths(repository)
|
||||
paths = (version_file, main_config_file, studio_config_file)
|
||||
original = {path: path.read_text(encoding="utf-8") for path in paths}
|
||||
current_dependency = f"reme-ai-studio=={current_version}"
|
||||
|
||||
main_text = original[main_config_file]
|
||||
if main_text.count(current_dependency) != 2:
|
||||
raise ValueError(f"Expected exactly two {current_dependency!r} pins in {main_config_file}.")
|
||||
updated = {
|
||||
version_file: _replace_version(original[version_file], _VERSION_PATTERN, version, version_file),
|
||||
main_config_file: main_text.replace(current_dependency, f"reme-ai-studio=={version}"),
|
||||
studio_config_file: _replace_version(
|
||||
original[studio_config_file],
|
||||
_STUDIO_VERSION_PATTERN,
|
||||
version,
|
||||
studio_config_file,
|
||||
),
|
||||
}
|
||||
|
||||
written: list[Path] = []
|
||||
try:
|
||||
for path in paths:
|
||||
_write_atomic(path, updated[path])
|
||||
written.append(path)
|
||||
check_versions(repository, version)
|
||||
except BaseException as error:
|
||||
rollback_errors: list[str] = []
|
||||
for path in reversed(written):
|
||||
try:
|
||||
_write_atomic(path, original[path])
|
||||
except OSError as rollback_error:
|
||||
rollback_errors.append(f"{path}: {rollback_error}")
|
||||
if rollback_errors:
|
||||
raise RuntimeError(
|
||||
"Version update failed and rollback was incomplete. Restore these files from Git:\n- "
|
||||
+ "\n- ".join(rollback_errors),
|
||||
) from error
|
||||
raise RuntimeError(
|
||||
"Version update failed; all changed files were restored. Fix the error and retry.",
|
||||
) from error
|
||||
return current_version
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run the version validation or update command."""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("version", nargs="?", help="New version for both distributions, for example 0.4.1.8")
|
||||
parser.add_argument("--check", action="store_true", help="Validate versions without changing files")
|
||||
parser.add_argument("--expected-version", help="Also require this release/tag version; a leading v is accepted")
|
||||
args = parser.parse_args()
|
||||
if args.check:
|
||||
if args.version:
|
||||
parser.error("version cannot be used with --check; use --expected-version")
|
||||
version = check_versions(expected_version=args.expected_version)
|
||||
print(f"Release versions are consistent: {version}")
|
||||
return
|
||||
if args.expected_version:
|
||||
parser.error("--expected-version requires --check")
|
||||
if not args.version:
|
||||
parser.error("provide a version to update, or use --check")
|
||||
previous_version = bump_version(args.version)
|
||||
print(f"Updated ReMe and ReMe Studio from {previous_version} to {args.version}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -10,6 +10,8 @@ REPOSITORY_DIR = Path(__file__).resolve().parents[1]
|
|||
WEBSITE_DIR = REPOSITORY_DIR / "website"
|
||||
PACKAGE_DIR = REPOSITORY_DIR / "packages" / "reme_ai_studio"
|
||||
STATIC_DIR = PACKAGE_DIR / "src" / "reme_ai_studio" / "static"
|
||||
LICENSE_FILE = REPOSITORY_DIR / "LICENSE"
|
||||
STATIC_GITIGNORE = "*\n!.gitignore\n"
|
||||
|
||||
_RAW_WEBSITE_URL = "https://raw.githubusercontent.com/agentscope-ai/ReMe/main/website"
|
||||
_REPOSITORY_URL = "https://github.com/agentscope-ai/ReMe"
|
||||
|
|
@ -35,15 +37,20 @@ def build_readme() -> str:
|
|||
|
||||
|
||||
def prepare_package(*, copy_static: bool = True) -> None:
|
||||
"""Generate the PyPI README and optionally stage the static build."""
|
||||
"""Generate package metadata files and optionally stage the static build."""
|
||||
(PACKAGE_DIR / "README.md").write_text(build_readme(), encoding="utf-8")
|
||||
shutil.copyfile(LICENSE_FILE, PACKAGE_DIR / "LICENSE")
|
||||
if not copy_static:
|
||||
return
|
||||
source = WEBSITE_DIR / "dist-static"
|
||||
if not (source / "index.html").is_file():
|
||||
raise FileNotFoundError(f"Studio static build is unavailable: {source}")
|
||||
shutil.rmtree(STATIC_DIR, ignore_errors=True)
|
||||
shutil.copytree(source, STATIC_DIR)
|
||||
try:
|
||||
shutil.rmtree(STATIC_DIR, ignore_errors=True)
|
||||
shutil.copytree(source, STATIC_DIR)
|
||||
finally:
|
||||
STATIC_DIR.mkdir(parents=True, exist_ok=True)
|
||||
(STATIC_DIR / ".gitignore").write_text(STATIC_GITIGNORE, encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
|
|
|||
|
|
@ -503,6 +503,11 @@ def test_daily_paper_topics_parameter_defaults_to_empty():
|
|||
}
|
||||
|
||||
|
||||
def test_daily_paper_cron_prioritizes_long_term_llm_memory():
|
||||
"""The scheduled workflow prioritizes papers about long-term LLM memory."""
|
||||
assert _load_config("daily_cookbook")["jobs"]["daily_paper_cron"]["topics"] == "大模型长期记忆"
|
||||
|
||||
|
||||
def test_daily_paper_hf_mirror_parameter_defaults_to_disabled():
|
||||
"""The public job schema exposes an explicit Hugging Face mirror switch."""
|
||||
use_hf_mirror = _load_config("daily_cookbook")["jobs"]["daily_paper"]["parameters"]["properties"]["use_hf_mirror"]
|
||||
|
|
@ -514,14 +519,14 @@ def test_daily_paper_hf_mirror_parameter_defaults_to_disabled():
|
|||
}
|
||||
|
||||
|
||||
def test_daily_paper_cron_hf_mirror_uses_explicit_environment_switch(monkeypatch):
|
||||
"""The scheduled workflow can opt in to the Hugging Face mirror."""
|
||||
def test_daily_paper_cron_hf_mirror_defaults_enabled_with_environment_override(monkeypatch):
|
||||
"""The scheduled workflow uses the mirror by default and supports an explicit override."""
|
||||
monkeypatch.delenv("DAILY_PAPER_USE_HF_MIRROR", raising=False)
|
||||
assert _load_config("daily_cookbook")["jobs"]["daily_paper_cron"]["use_hf_mirror"] is False
|
||||
|
||||
monkeypatch.setenv("DAILY_PAPER_USE_HF_MIRROR", "true")
|
||||
assert _load_config("daily_cookbook")["jobs"]["daily_paper_cron"]["use_hf_mirror"] is True
|
||||
|
||||
monkeypatch.setenv("DAILY_PAPER_USE_HF_MIRROR", "false")
|
||||
assert _load_config("daily_cookbook")["jobs"]["daily_paper_cron"]["use_hf_mirror"] is False
|
||||
|
||||
|
||||
def test_paper_pick_list_uses_an_object_root_for_tool_output():
|
||||
"""AgentScope function arguments require an object-root JSON schema."""
|
||||
|
|
|
|||
|
|
@ -1,28 +1,204 @@
|
|||
"""Keep the separately published distributions version-compatible."""
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import tomllib
|
||||
from types import ModuleType
|
||||
|
||||
import reme
|
||||
from scripts.package_studio import build_readme
|
||||
import pytest
|
||||
|
||||
REPOSITORY = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _load_script(name: str) -> ModuleType:
|
||||
script = REPOSITORY / "scripts" / f"{name}.py"
|
||||
spec = importlib.util.spec_from_file_location(name, script)
|
||||
if spec is None or spec.loader is None:
|
||||
raise ImportError(f"Unable to load {script}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
bump_version = _load_script("bump_version")
|
||||
package_studio = _load_script("package_studio")
|
||||
|
||||
|
||||
def test_studio_package_and_extra_versions_match_reme() -> None:
|
||||
"""Require one version bump to update both wheels and their exact pin."""
|
||||
repository = Path(__file__).resolve().parents[2]
|
||||
main_config = tomllib.loads((repository / "pyproject.toml").read_text(encoding="utf-8"))
|
||||
main_config = tomllib.loads((REPOSITORY / "pyproject.toml").read_text(encoding="utf-8"))
|
||||
studio_config = tomllib.loads(
|
||||
(repository / "packages" / "reme_ai_studio" / "pyproject.toml").read_text(encoding="utf-8"),
|
||||
(REPOSITORY / "packages" / "reme_ai_studio" / "pyproject.toml").read_text(encoding="utf-8"),
|
||||
)
|
||||
expected_dependency = f"reme-ai-studio=={reme.__version__}"
|
||||
version = bump_version.read_version(REPOSITORY)
|
||||
expected_dependency = f"reme-ai-studio=={version}"
|
||||
|
||||
assert studio_config["project"]["version"] == reme.__version__
|
||||
assert studio_config["project"]["version"] == version
|
||||
assert main_config["project"]["optional-dependencies"]["web"] == [expected_dependency]
|
||||
assert expected_dependency in main_config["project"]["optional-dependencies"]["core"]
|
||||
|
||||
|
||||
def _write_version_fixture(repository: Path, *, studio_version: str = "1.2.3") -> None:
|
||||
(repository / "reme").mkdir()
|
||||
(repository / "packages" / "reme_ai_studio").mkdir(parents=True)
|
||||
(repository / "reme" / "__init__.py").write_text('__version__ = "1.2.3"\n', encoding="utf-8")
|
||||
(repository / "pyproject.toml").write_text(
|
||||
"""[project]
|
||||
name = "reme-ai"
|
||||
|
||||
[project.optional-dependencies]
|
||||
web = ["reme-ai-studio==1.2.3"]
|
||||
core = ["example", "reme-ai-studio==1.2.3"]
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(repository / "packages" / "reme_ai_studio" / "pyproject.toml").write_text(
|
||||
f'[project]\nname = "reme-ai-studio"\nversion = "{studio_version}"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def test_bump_version_updates_both_packages_and_exact_pins(tmp_path: Path) -> None:
|
||||
"""Update the two package versions and both dependency declarations together."""
|
||||
_write_version_fixture(tmp_path)
|
||||
|
||||
previous_version = bump_version.bump_version("1.2.4", tmp_path)
|
||||
|
||||
assert previous_version == "1.2.3"
|
||||
assert (tmp_path / "reme" / "__init__.py").read_text(encoding="utf-8") == '__version__ = "1.2.4"\n'
|
||||
assert (tmp_path / "pyproject.toml").read_text(encoding="utf-8").count("reme-ai-studio==1.2.4") == 2
|
||||
studio_config = tomllib.loads(
|
||||
(tmp_path / "packages" / "reme_ai_studio" / "pyproject.toml").read_text(encoding="utf-8"),
|
||||
)
|
||||
assert studio_config["project"]["version"] == "1.2.4"
|
||||
|
||||
|
||||
def test_bump_version_rejects_inconsistent_sources_before_writing(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Refuse to update any file if the current release metadata has drifted."""
|
||||
_write_version_fixture(tmp_path, studio_version="1.2.2")
|
||||
version_file = tmp_path / "reme" / "__init__.py"
|
||||
original_version_text = version_file.read_text(encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match=r"project.version is '1.2.2'; expected '1.2.3'"):
|
||||
bump_version.bump_version("1.2.4", tmp_path)
|
||||
|
||||
assert version_file.read_text(encoding="utf-8") == original_version_text
|
||||
|
||||
|
||||
@pytest.mark.parametrize("version", ["release", "1..2", "1.2.3_"])
|
||||
def test_bump_version_rejects_invalid_pep440_versions(tmp_path: Path, version: str) -> None:
|
||||
"""Reject invalid release versions before changing any source file."""
|
||||
_write_version_fixture(tmp_path)
|
||||
version_file = tmp_path / "reme" / "__init__.py"
|
||||
original_version_text = version_file.read_text(encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid PEP 440 version"):
|
||||
bump_version.bump_version(version, tmp_path)
|
||||
|
||||
assert version_file.read_text(encoding="utf-8") == original_version_text
|
||||
|
||||
|
||||
def test_check_versions_reports_release_tag_mismatch(tmp_path: Path) -> None:
|
||||
"""Explain which source must change when a release tag does not match."""
|
||||
_write_version_fixture(tmp_path)
|
||||
|
||||
with pytest.raises(ValueError, match=r"package version is '1.2.3'; release expects '1.2.4'"):
|
||||
bump_version.check_versions(tmp_path, "v1.2.4")
|
||||
|
||||
|
||||
def test_read_version_identifies_invalid_source_file(tmp_path: Path) -> None:
|
||||
"""Point maintainers to the invalid Python version declaration."""
|
||||
_write_version_fixture(tmp_path)
|
||||
version_file = tmp_path / "reme" / "__init__.py"
|
||||
version_file.write_text('__version__ = "1..2"\n', encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match=rf"{version_file}.*Fix the __version__ declaration"):
|
||||
bump_version.check_versions(tmp_path)
|
||||
|
||||
|
||||
def test_check_versions_identifies_invalid_toml_file(tmp_path: Path) -> None:
|
||||
"""Point maintainers to malformed package metadata."""
|
||||
_write_version_fixture(tmp_path)
|
||||
config_file = tmp_path / "pyproject.toml"
|
||||
config_file.write_text("[project\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match=rf"{config_file}.*cannot read valid TOML.*Fix this file"):
|
||||
bump_version.check_versions(tmp_path)
|
||||
|
||||
|
||||
def test_check_versions_identifies_missing_table(tmp_path: Path) -> None:
|
||||
"""Explain which required TOML table must be restored."""
|
||||
_write_version_fixture(tmp_path)
|
||||
config_file = tmp_path / "pyproject.toml"
|
||||
config_file.write_text('[project]\nname = "reme-ai"\n', encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match=rf"{config_file}.*\[project.optional-dependencies\].*Add or fix"):
|
||||
bump_version.check_versions(tmp_path)
|
||||
|
||||
|
||||
def test_bump_version_rolls_back_when_a_write_fails(monkeypatch, tmp_path: Path) -> None:
|
||||
"""Restore earlier files when a later atomic replacement fails."""
|
||||
# pylint: disable=protected-access
|
||||
_write_version_fixture(tmp_path)
|
||||
paths = bump_version._paths(tmp_path)
|
||||
original = {path: path.read_text(encoding="utf-8") for path in paths}
|
||||
write_atomic = bump_version._write_atomic
|
||||
attempts = 0
|
||||
|
||||
def fail_second_write(path: Path, content: str) -> None:
|
||||
nonlocal attempts
|
||||
attempts += 1
|
||||
if attempts == 2:
|
||||
raise OSError("simulated write failure")
|
||||
write_atomic(path, content)
|
||||
|
||||
monkeypatch.setattr(bump_version, "_write_atomic", fail_second_write)
|
||||
|
||||
with pytest.raises(RuntimeError, match="all changed files were restored"):
|
||||
bump_version.bump_version("1.2.4", tmp_path)
|
||||
|
||||
assert {path: path.read_text(encoding="utf-8") for path in paths} == original
|
||||
|
||||
|
||||
def test_studio_readme_is_generated_from_website_docs() -> None:
|
||||
"""Keep the packaged PyPI description synchronized with the source docs."""
|
||||
repository = Path(__file__).resolve().parents[2]
|
||||
packaged_readme = repository / "packages" / "reme_ai_studio" / "README.md"
|
||||
assert packaged_readme.read_text(encoding="utf-8") == build_readme()
|
||||
packaged_readme = REPOSITORY / "packages" / "reme_ai_studio" / "README.md"
|
||||
assert packaged_readme.read_text(encoding="utf-8") == package_studio.build_readme()
|
||||
|
||||
|
||||
def test_studio_package_preparation_copies_license(monkeypatch, tmp_path: Path) -> None:
|
||||
"""Include the repository license in the independently distributed Studio package."""
|
||||
package_dir = tmp_path / "reme_ai_studio"
|
||||
package_dir.mkdir()
|
||||
monkeypatch.setattr(package_studio, "PACKAGE_DIR", package_dir)
|
||||
|
||||
package_studio.prepare_package(copy_static=False)
|
||||
|
||||
assert (package_dir / "LICENSE").read_text(encoding="utf-8") == (REPOSITORY / "LICENSE").read_text(
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def test_studio_package_preparation_preserves_static_gitignore(monkeypatch, tmp_path: Path) -> None:
|
||||
"""Keep generated static assets ignored after staging the Studio build."""
|
||||
package_dir = tmp_path / "reme_ai_studio"
|
||||
package_dir.mkdir()
|
||||
website_dir = tmp_path / "website"
|
||||
source_dir = website_dir / "dist-static"
|
||||
source_dir.mkdir(parents=True)
|
||||
(source_dir / "index.html").write_text("<html></html>", encoding="utf-8")
|
||||
static_dir = package_dir / "src" / "reme_ai_studio" / "static"
|
||||
static_dir.mkdir(parents=True)
|
||||
(static_dir / ".gitignore").write_text(package_studio.STATIC_GITIGNORE, encoding="utf-8")
|
||||
|
||||
monkeypatch.setattr(package_studio, "PACKAGE_DIR", package_dir)
|
||||
monkeypatch.setattr(package_studio, "WEBSITE_DIR", website_dir)
|
||||
monkeypatch.setattr(package_studio, "STATIC_DIR", static_dir)
|
||||
monkeypatch.setattr(package_studio, "build_readme", lambda: "# ReMe Studio\n")
|
||||
|
||||
package_studio.prepare_package()
|
||||
|
||||
assert (static_dir / "index.html").is_file()
|
||||
assert (static_dir / ".gitignore").read_text(encoding="utf-8") == package_studio.STATIC_GITIGNORE
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue