Compare commits
97 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1de31ab2c | ||
|
|
e4c381c7b1 | ||
|
|
084c02e43a | ||
|
|
dc12798526 | ||
|
|
090c8c24ab | ||
|
|
eb2f466a30 | ||
|
|
4c54c2b650 | ||
|
|
49bbbc93ff | ||
|
|
d529ec5256 | ||
|
|
1648b7ce93 | ||
|
|
65b48ec5fc | ||
|
|
81c16d3e2c | ||
|
|
67135cfc57 | ||
|
|
67936d5a43 | ||
|
|
30227e509f | ||
|
|
bebad36745 | ||
|
|
8b5456641f | ||
|
|
c9a9728164 | ||
|
|
ce386f5391 | ||
|
|
873bcee220 | ||
|
|
19472233be | ||
|
|
59e4ed7d3b | ||
|
|
07d4d6e838 | ||
|
|
6125fc197d | ||
|
|
cae613d1c4 | ||
|
|
5231f3970c | ||
|
|
9ebe17a89d | ||
|
|
16269c9a76 | ||
|
|
6c5d2194c0 | ||
|
|
5f2c693ddb | ||
|
|
dab56fc794 | ||
|
|
6f4bdfd416 | ||
|
|
b9caae1e50 | ||
|
|
d67f1490f5 | ||
|
|
fe336da566 | ||
|
|
fd6337fe42 | ||
|
|
6ed97f033b | ||
|
|
46eca95bb9 | ||
|
|
4f7c8786e3 | ||
|
|
a518dd168b | ||
|
|
9ad3dafce5 | ||
|
|
05958d4d8b | ||
|
|
dff2d33cec | ||
|
|
6cb81e7921 | ||
|
|
1be61b1e4c | ||
|
|
7e25d4679b | ||
|
|
9975bb37b9 | ||
|
|
06fb46fa48 | ||
|
|
1f67a6ce29 | ||
|
|
354837f9af | ||
|
|
f04eedb3ab | ||
|
|
36e3a87c75 | ||
|
|
5c17874f73 | ||
|
|
0eba6ea831 | ||
|
|
193fd418fb | ||
|
|
8c3d3016e3 | ||
|
|
dc28e62526 | ||
|
|
88ed21165b | ||
|
|
3f2eb6235f | ||
|
|
8c4898999d | ||
|
|
c1b85f8241 | ||
|
|
f9a45a319a | ||
|
|
65cb4ebdd6 | ||
|
|
bec7e48772 | ||
|
|
fc4a5398a8 | ||
|
|
21f7757c80 | ||
|
|
c85917a812 | ||
|
|
157b096448 | ||
|
|
99afc2604f | ||
|
|
2dd2255760 | ||
|
|
d8d667c6ac | ||
|
|
940a923f06 | ||
|
|
3d2ecc60d2 | ||
|
|
6f38d201b6 | ||
|
|
ef3f99f019 | ||
|
|
b78e32ef03 | ||
|
|
6a6e0b3c29 | ||
|
|
a457bf7542 | ||
|
|
513fb5b7f4 | ||
|
|
1a6b584274 | ||
|
|
15d12be6b6 | ||
|
|
626c850ccb | ||
|
|
01ef1a6efb | ||
|
|
efcc2b34d1 | ||
|
|
c8e1248769 | ||
|
|
8416fd3ac9 | ||
|
|
f44f52d919 | ||
|
|
ebcb154e37 | ||
|
|
39233f4e62 | ||
|
|
94b7dedc26 | ||
|
|
87187c1d25 | ||
|
|
f5ec230fef | ||
|
|
2f5fd46b44 | ||
|
|
618e8cec66 | ||
|
|
d3aee1adf5 | ||
|
|
6b9a75267b | ||
|
|
c792fd197c |
40
.dockerignore
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
.git
|
||||||
|
**/.DS_Store
|
||||||
|
**/.ssh
|
||||||
|
**/.codex
|
||||||
|
**/.claude
|
||||||
|
**/private*
|
||||||
|
**/.env
|
||||||
|
**/.env.*
|
||||||
|
**/.reme
|
||||||
|
**/.venv
|
||||||
|
**/venv
|
||||||
|
**/__pycache__
|
||||||
|
**/*.py[cod]
|
||||||
|
**/.pytest_cache
|
||||||
|
**/.mypy_cache
|
||||||
|
**/.ruff_cache
|
||||||
|
**/.coverage*
|
||||||
|
**/htmlcov
|
||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
**/dist-static
|
||||||
|
**/.generated
|
||||||
|
**/.next
|
||||||
|
**/.vite
|
||||||
|
**/.wrangler
|
||||||
|
**/.cache
|
||||||
|
**/*.egg-info
|
||||||
|
**/build
|
||||||
|
**/logs
|
||||||
|
**/*.log
|
||||||
|
**/*.tmp
|
||||||
|
reme_studio/src/reme_studio/static
|
||||||
|
benchmark
|
||||||
|
cookbook
|
||||||
|
docs
|
||||||
|
github-pages
|
||||||
|
integrations
|
||||||
|
plugins
|
||||||
|
skills
|
||||||
|
tests
|
||||||
97
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
name: Bug report
|
||||||
|
description: Report reproducible incorrect or unexpected ReMe behavior
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: [bug]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for helping improve ReMe. Please remove secrets, API keys, and private memory content before submitting.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: What happened, and what did you expect instead?
|
||||||
|
placeholder: Describe the observed and expected behavior.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Provide the smallest configuration and command sequence that reproduces the problem.
|
||||||
|
placeholder: |
|
||||||
|
1. Configure ...
|
||||||
|
2. Run ...
|
||||||
|
3. Observe ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: config
|
||||||
|
attributes:
|
||||||
|
label: Relevant configuration
|
||||||
|
description: Include only relevant values and redact credentials, tokens, endpoints, and private paths.
|
||||||
|
render: yaml
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Logs or traceback
|
||||||
|
description: Paste relevant output after removing secrets and private workspace content.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: reme-version
|
||||||
|
attributes:
|
||||||
|
label: ReMe version
|
||||||
|
placeholder: e.g. 0.4.1.8 or a commit SHA
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: python-version
|
||||||
|
attributes:
|
||||||
|
label: Python version
|
||||||
|
placeholder: e.g. 3.11.9
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating system
|
||||||
|
options:
|
||||||
|
- Linux
|
||||||
|
- macOS
|
||||||
|
- Windows
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: area
|
||||||
|
attributes:
|
||||||
|
label: Affected area
|
||||||
|
options:
|
||||||
|
- CLI or configuration
|
||||||
|
- HTTP, MCP, or local service
|
||||||
|
- Memory or workspace files
|
||||||
|
- Search, catalog, graph, or index
|
||||||
|
- Model or agent integration
|
||||||
|
- ReMe Studio
|
||||||
|
- Plugin or external integration
|
||||||
|
- Packaging or installation
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: safety
|
||||||
|
attributes:
|
||||||
|
label: Data safety
|
||||||
|
options:
|
||||||
|
- label: I removed credentials and private memory content from this report.
|
||||||
|
required: true
|
||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: ReMe documentation
|
||||||
|
url: https://reme.agentscope.io
|
||||||
|
about: Read the installation, configuration, and usage guides.
|
||||||
|
- name: Existing issues
|
||||||
|
url: https://github.com/agentscope-ai/ReMe/issues
|
||||||
|
about: Search for existing reports and discussions before opening a new issue.
|
||||||
64
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
name: Feature request
|
||||||
|
description: Propose a focused enhancement to ReMe
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels: [enhancement]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Problem
|
||||||
|
description: What user problem or limitation should this change address?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: proposal
|
||||||
|
attributes:
|
||||||
|
label: Proposed behavior
|
||||||
|
description: Describe the desired behavior and its user-visible contract.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: area
|
||||||
|
attributes:
|
||||||
|
label: Area
|
||||||
|
options:
|
||||||
|
- CLI or configuration
|
||||||
|
- Jobs or steps
|
||||||
|
- Memory or workspace files
|
||||||
|
- Search, catalog, graph, or index
|
||||||
|
- Service or client
|
||||||
|
- Model or agent integration
|
||||||
|
- ReMe Studio
|
||||||
|
- Plugin or external integration
|
||||||
|
- Documentation
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: ownership
|
||||||
|
attributes:
|
||||||
|
label: Local-first and compatibility considerations
|
||||||
|
description: Explain any effect on user-owned files, rebuildable state, configuration, schemas, or service interfaces.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives considered
|
||||||
|
description: Describe workarounds or alternative designs you considered.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: examples
|
||||||
|
attributes:
|
||||||
|
label: Example usage
|
||||||
|
description: Show the proposed CLI, configuration, API, or UI behavior when useful.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: contribution
|
||||||
|
attributes:
|
||||||
|
label: Contribution
|
||||||
|
options:
|
||||||
|
- label: I am willing to help implement or test this feature.
|
||||||
53
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
name: Usage question
|
||||||
|
description: Ask for help using or configuring ReMe
|
||||||
|
title: "[Question]: "
|
||||||
|
labels: [question]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Please check the documentation and existing issues before asking a new question.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: goal
|
||||||
|
attributes:
|
||||||
|
label: What are you trying to achieve?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: attempted
|
||||||
|
attributes:
|
||||||
|
label: What have you tried?
|
||||||
|
description: Include relevant commands or configuration, with secrets and private memory content removed.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: reme-version
|
||||||
|
attributes:
|
||||||
|
label: ReMe version
|
||||||
|
placeholder: e.g. 0.4.1.8 or a commit SHA
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: area
|
||||||
|
attributes:
|
||||||
|
label: Area
|
||||||
|
options:
|
||||||
|
- Installation
|
||||||
|
- Configuration
|
||||||
|
- CLI or service usage
|
||||||
|
- Memory and workspace management
|
||||||
|
- Search and retrieval
|
||||||
|
- ReMe Studio
|
||||||
|
- Plugin or integration
|
||||||
|
- Other
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: checked
|
||||||
|
attributes:
|
||||||
|
label: Before submitting
|
||||||
|
options:
|
||||||
|
- label: I checked the [ReMe documentation](https://reme.agentscope.io) and searched existing issues.
|
||||||
|
required: true
|
||||||
|
- label: I removed credentials and private memory content.
|
||||||
|
required: true
|
||||||
35
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
<!-- Explain the problem and the smallest coherent change that addresses it. -->
|
||||||
|
|
||||||
|
## Related issue
|
||||||
|
|
||||||
|
<!-- Use "Fixes #123" when applicable. -->
|
||||||
|
|
||||||
|
## Contract and data impact
|
||||||
|
|
||||||
|
- [ ] No public configuration, schema, CLI, endpoint, streaming, or workspace-layout contract changes
|
||||||
|
- [ ] No user-owned memory files are deleted or rewritten
|
||||||
|
- [ ] Derived indexes, catalogs, graphs, caches, and metadata remain rebuildable
|
||||||
|
|
||||||
|
<!-- If any item is unchecked, describe the impact and migration or recovery path. -->
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
<!-- List the exact checks run and their results. Explain relevant checks that were not run. -->
|
||||||
|
|
||||||
|
- [ ] Focused tests pass
|
||||||
|
- [ ] Unit tests pass, or omitted tests are explained below
|
||||||
|
- [ ] `pre-commit run --all-files` passes, or omitted checks are explained below
|
||||||
|
- [ ] Frontend checks were run when `reme_studio/` changed
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] I reviewed the diff for unrelated changes and sensitive data
|
||||||
|
- [ ] Tests cover intentional behavior changes
|
||||||
|
- [ ] Defaults, schemas, and concise documentation were updated together when required
|
||||||
|
- [ ] Long-lived clients, tasks, services, and executors follow the application lifecycle
|
||||||
|
|
||||||
|
## Screenshots or additional notes
|
||||||
|
|
||||||
|
<!-- Include UI screenshots, compatibility notes, or follow-up work when relevant. -->
|
||||||
35
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
target-branch: "main"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
day: "monday"
|
||||||
|
time: "09:30"
|
||||||
|
timezone: "Asia/Shanghai"
|
||||||
|
groups:
|
||||||
|
codeql:
|
||||||
|
patterns:
|
||||||
|
- "github/codeql-action/*"
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
commit-message:
|
||||||
|
prefix: "chore"
|
||||||
|
include: "scope"
|
||||||
|
|
||||||
|
- package-ecosystem: "pip"
|
||||||
|
directory: "/"
|
||||||
|
target-branch: "main"
|
||||||
|
schedule:
|
||||||
|
interval: "cron"
|
||||||
|
cronjob: "30 9 * * *"
|
||||||
|
timezone: "Asia/Shanghai"
|
||||||
|
allow:
|
||||||
|
- dependency-name: "agentscope"
|
||||||
|
cooldown:
|
||||||
|
exclude:
|
||||||
|
- "agentscope"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
commit-message:
|
||||||
|
prefix: "chore"
|
||||||
|
include: "scope"
|
||||||
80
.github/workflows/_build-docs.yml
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
name: _Build documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
run_tests:
|
||||||
|
description: Run the documentation test suite before building
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
upload_pages_artifact:
|
||||||
|
description: Upload the build for a later GitHub Pages deployment job
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: github-pages
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: '22.22.3'
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: |
|
||||||
|
github-pages/package-lock.json
|
||||||
|
reme_studio/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Install Studio dependencies
|
||||||
|
run: npm ci --prefix ../reme_studio
|
||||||
|
|
||||||
|
- name: Check Studio types and lint
|
||||||
|
if: inputs.run_tests
|
||||||
|
working-directory: reme_studio
|
||||||
|
run: npm run format:check && npm run lint
|
||||||
|
|
||||||
|
- name: Test browser demo behavior
|
||||||
|
if: inputs.run_tests
|
||||||
|
working-directory: reme_studio
|
||||||
|
run: node --test tests/demo-workspace.test.mjs tests/wikilinks.test.mjs
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
if: inputs.run_tests
|
||||||
|
run: npm test
|
||||||
|
|
||||||
|
- name: Build documentation
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Verify browser demo bundle
|
||||||
|
if: inputs.run_tests
|
||||||
|
working-directory: reme_studio
|
||||||
|
run: node --test tests/demo-build.test.mjs
|
||||||
|
|
||||||
|
- name: Configure Pages
|
||||||
|
if: inputs.upload_pages_artifact
|
||||||
|
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6
|
||||||
|
|
||||||
|
- name: Upload Pages artifact
|
||||||
|
if: inputs.upload_pages_artifact
|
||||||
|
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
||||||
|
with:
|
||||||
|
path: github-pages/dist
|
||||||
89
.github/workflows/_build-python-packages.yml
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
name: _Build Python packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
expected_version:
|
||||||
|
description: Expected release version; omit for a consistency-only check
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
type: string
|
||||||
|
upload_artifacts:
|
||||||
|
description: Upload distributions for later publish jobs
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
distributions:
|
||||||
|
name: Build Python distributions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
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 package versions
|
||||||
|
if: inputs.expected_version == ''
|
||||||
|
run: python scripts/bump_version.py --check
|
||||||
|
|
||||||
|
- name: Validate release version
|
||||||
|
if: inputs.expected_version != ''
|
||||||
|
env:
|
||||||
|
EXPECTED_VERSION: ${{ inputs.expected_version }}
|
||||||
|
run: python scripts/bump_version.py --check --expected-version "${EXPECTED_VERSION}"
|
||||||
|
|
||||||
|
- name: Run package tests
|
||||||
|
run: PYTHONPATH=. python -m pytest tests/unit/test_package_versions.py -q
|
||||||
|
|
||||||
|
- name: Build and check distributions
|
||||||
|
run: |
|
||||||
|
mkdir -p dist/reme
|
||||||
|
python -m build --outdir dist/reme
|
||||||
|
python -m twine check dist/reme/*
|
||||||
|
|
||||||
|
- name: Verify distributions and isolated installation
|
||||||
|
run: |
|
||||||
|
REME_WHEEL="$(pwd)/$(ls dist/reme/reme_ai-[0-9]*.whl)"
|
||||||
|
python -m zipfile -l "${REME_WHEEL}" | (! grep 'reme/web/')
|
||||||
|
python -m zipfile -l "${REME_WHEEL}" | (! grep 'reme_studio/')
|
||||||
|
python -m venv "${RUNNER_TEMP}/reme-package-smoke"
|
||||||
|
"${RUNNER_TEMP}/reme-package-smoke/bin/python" -m pip install "${REME_WHEEL}[as]"
|
||||||
|
cd "${RUNNER_TEMP}"
|
||||||
|
"${RUNNER_TEMP}/reme-package-smoke/bin/python" -c "import reme"
|
||||||
|
|
||||||
|
- name: Verify released core dependencies
|
||||||
|
if: inputs.expected_version != ''
|
||||||
|
run: |
|
||||||
|
REME_WHEEL="$(pwd)/$(ls dist/reme/reme_ai-[0-9]*.whl)"
|
||||||
|
python -m venv "${RUNNER_TEMP}/reme-core-package-smoke"
|
||||||
|
"${RUNNER_TEMP}/reme-core-package-smoke/bin/python" -m pip install "${REME_WHEEL}[core]"
|
||||||
|
cd "${RUNNER_TEMP}"
|
||||||
|
"${RUNNER_TEMP}/reme-core-package-smoke/bin/python" - <<'PY'
|
||||||
|
from reme_studio import static_dir
|
||||||
|
|
||||||
|
assert (static_dir() / "index.html").is_file()
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Upload ReMe distributions
|
||||||
|
if: inputs.upload_artifacts
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: reme-distributions
|
||||||
|
path: dist/reme/
|
||||||
|
if-no-files-found: error
|
||||||
155
.github/workflows/_release-npm-plugin.yml
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
name: _Release npm plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
directory:
|
||||||
|
description: Repository-relative package directory
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
package_name:
|
||||||
|
description: Exact public npm package name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
artifact_name:
|
||||||
|
description: Prefix for the packed package artifact
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
version:
|
||||||
|
description: Exact package.json version; an optional v prefix is accepted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
npm_tag:
|
||||||
|
description: npm distribution tag
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
use_npm_token:
|
||||||
|
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
validate_clawhub:
|
||||||
|
description: Validate the package against the ClawHub contract
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
outputs:
|
||||||
|
version:
|
||||||
|
description: Normalized package version
|
||||||
|
value: ${{ jobs.build.outputs.version }}
|
||||||
|
secrets:
|
||||||
|
NPM_TOKEN:
|
||||||
|
description: Optional bootstrap or recovery token for npm publishing
|
||||||
|
required: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.validate.outputs.version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "24.16.0"
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: ${{ inputs.directory }}/package-lock.json
|
||||||
|
|
||||||
|
- name: Validate package identity and version
|
||||||
|
id: validate
|
||||||
|
working-directory: ${{ inputs.directory }}
|
||||||
|
env:
|
||||||
|
EXPECTED_NAME: ${{ inputs.package_name }}
|
||||||
|
RELEASE_VERSION: ${{ inputs.version }}
|
||||||
|
NPM_TAG: ${{ inputs.npm_tag }}
|
||||||
|
run: |
|
||||||
|
node --input-type=module <<'JS'
|
||||||
|
import { appendFileSync, readFileSync } from 'node:fs';
|
||||||
|
const manifest = JSON.parse(readFileSync('package.json', 'utf8'));
|
||||||
|
const expected = process.env.RELEASE_VERSION.replace(/^v/, '');
|
||||||
|
if (manifest.name !== process.env.EXPECTED_NAME) {
|
||||||
|
throw new Error(`Expected ${process.env.EXPECTED_NAME}, found ${manifest.name}`);
|
||||||
|
}
|
||||||
|
if (manifest.version !== expected) throw new Error(`package.json is ${manifest.version}, workflow input is ${expected}`);
|
||||||
|
if (manifest.version.includes('-') !== (process.env.NPM_TAG === 'next')) {
|
||||||
|
throw new Error('Prereleases must use next; stable releases must use latest');
|
||||||
|
}
|
||||||
|
appendFileSync(process.env.GITHUB_OUTPUT, `version=${manifest.version}\n`);
|
||||||
|
JS
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
working-directory: ${{ inputs.directory }}
|
||||||
|
- name: Validate package
|
||||||
|
working-directory: ${{ inputs.directory }}
|
||||||
|
run: |
|
||||||
|
npm run format:check
|
||||||
|
npm run lint
|
||||||
|
npm run typecheck
|
||||||
|
npm test
|
||||||
|
npm run test:package
|
||||||
|
- name: Validate ClawHub contract
|
||||||
|
if: inputs.validate_clawhub
|
||||||
|
working-directory: ${{ inputs.directory }}
|
||||||
|
run: npx --yes clawhub@0.23.3 package validate . --json
|
||||||
|
- name: Pack
|
||||||
|
working-directory: ${{ inputs.directory }}
|
||||||
|
run: |
|
||||||
|
mkdir -p "$RUNNER_TEMP/plugin-package"
|
||||||
|
npm pack --pack-destination "$RUNNER_TEMP/plugin-package"
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.artifact_name }}-${{ steps.validate.outputs.version }}
|
||||||
|
path: ${{ runner.temp }}/plugin-package/*.tgz
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
publish:
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: npm
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "24"
|
||||||
|
registry-url: https://registry.npmjs.org
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.artifact_name }}-${{ needs.build.outputs.version }}
|
||||||
|
path: dist/plugin
|
||||||
|
- name: Reject an existing package version
|
||||||
|
env:
|
||||||
|
PACKAGE_NAME: ${{ inputs.package_name }}
|
||||||
|
PACKAGE_VERSION: ${{ needs.build.outputs.version }}
|
||||||
|
run: |
|
||||||
|
if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
|
||||||
|
echo "${PACKAGE_NAME}@${PACKAGE_VERSION} already exists" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Publish to npm with Trusted Publishing
|
||||||
|
if: ${{ !inputs.use_npm_token }}
|
||||||
|
env:
|
||||||
|
NPM_TAG: ${{ inputs.npm_tag }}
|
||||||
|
run: npm publish dist/plugin/*.tgz --access public --tag "$NPM_TAG" --provenance
|
||||||
|
- name: Publish to npm with NPM_TOKEN
|
||||||
|
if: inputs.use_npm_token
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
NPM_TAG: ${{ inputs.npm_tag }}
|
||||||
|
run: |
|
||||||
|
if [[ -z "${NODE_AUTH_TOKEN}" ]]; then
|
||||||
|
echo "NPM_TOKEN is required when use_npm_token is enabled" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
npm publish dist/plugin/*.tgz --access public --tag "$NPM_TAG" --provenance
|
||||||
40
.github/workflows/ci-docs.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: CI / Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/ci-docs.yml'
|
||||||
|
- '.github/workflows/_build-docs.yml'
|
||||||
|
- 'AGENTS.md'
|
||||||
|
- 'README.md'
|
||||||
|
- 'README_ZH.md'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'github-pages/**'
|
||||||
|
- 'reme/config/default.yaml'
|
||||||
|
- 'integrations/claude_code/README.md'
|
||||||
|
- 'integrations/hermes_agent/README.md'
|
||||||
|
- 'integrations/hermes_agent/figures/**'
|
||||||
|
- 'reme_studio/**'
|
||||||
|
- 'integrations/dsh/README*.md'
|
||||||
|
- 'integrations/dsh/figures/**'
|
||||||
|
- 'integrations/openclaw/README*.md'
|
||||||
|
- 'integrations/openclaw/figures/**'
|
||||||
|
- 'plugins/*/README*.md'
|
||||||
|
- 'benchmark/*/README*.md'
|
||||||
|
- 'benchmark/toolmemory/gitcha.png'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
documentation:
|
||||||
|
name: Test and build documentation
|
||||||
|
uses: ./.github/workflows/_build-docs.yml
|
||||||
|
with:
|
||||||
|
run_tests: true
|
||||||
52
.github/workflows/ci-dsh-plugin.yml
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
name: CI / DSH plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/ci-dsh-plugin.yml"
|
||||||
|
- ".github/workflows/_release-npm-plugin.yml"
|
||||||
|
- ".github/workflows/release-dsh-plugin.yml"
|
||||||
|
- "integrations/dsh/**"
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/ci-dsh-plugin.yml"
|
||||||
|
- ".github/workflows/_release-npm-plugin.yml"
|
||||||
|
- ".github/workflows/release-dsh-plugin.yml"
|
||||||
|
- "integrations/dsh/**"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
package:
|
||||||
|
name: Validate DSH plugin
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: integrations/dsh
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "24.16.0"
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: integrations/dsh/package-lock.json
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run format:check
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run typecheck
|
||||||
|
- run: npm test
|
||||||
|
- run: npm run test:package
|
||||||
54
.github/workflows/ci-openclaw-plugin.yml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
name: CI / OpenClaw plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/ci-openclaw-plugin.yml"
|
||||||
|
- ".github/workflows/_release-npm-plugin.yml"
|
||||||
|
- ".github/workflows/release-openclaw-plugin.yml"
|
||||||
|
- "integrations/openclaw/**"
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/ci-openclaw-plugin.yml"
|
||||||
|
- ".github/workflows/_release-npm-plugin.yml"
|
||||||
|
- ".github/workflows/release-openclaw-plugin.yml"
|
||||||
|
- "integrations/openclaw/**"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
package:
|
||||||
|
name: Validate OpenClaw plugin
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: integrations/openclaw
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "24.16.0"
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: integrations/openclaw/package-lock.json
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run format:check
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run typecheck
|
||||||
|
- run: npm test
|
||||||
|
- run: npm run test:package
|
||||||
|
- name: Validate ClawHub contract
|
||||||
|
run: npx --yes clawhub@0.23.3 package validate . --json
|
||||||
40
.github/workflows/ci-packages.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: CI / Python packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/ci-packages.yml'
|
||||||
|
- '.github/workflows/_build-python-packages.yml'
|
||||||
|
- '.github/workflows/release-python.yml'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'README.md'
|
||||||
|
- 'reme/**'
|
||||||
|
- 'scripts/bump_version.py'
|
||||||
|
- 'tests/unit/test_package_versions.py'
|
||||||
|
- 'LICENSE'
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/ci-packages.yml'
|
||||||
|
- '.github/workflows/_build-python-packages.yml'
|
||||||
|
- '.github/workflows/release-python.yml'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'README.md'
|
||||||
|
- 'reme/**'
|
||||||
|
- 'scripts/bump_version.py'
|
||||||
|
- 'tests/unit/test_package_versions.py'
|
||||||
|
- 'LICENSE'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
distributions:
|
||||||
|
name: Build and verify distributions
|
||||||
|
uses: ./.github/workflows/_build-python-packages.yml
|
||||||
65
.github/workflows/ci-python-quality.yml
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
name: CI / Python quality
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
actionlint:
|
||||||
|
name: GitHub Actions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Validate workflows with actionlint
|
||||||
|
env:
|
||||||
|
ACTIONLINT_VERSION: 1.7.12
|
||||||
|
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
|
||||||
|
run: |
|
||||||
|
archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
|
||||||
|
curl --fail --location --proto '=https' --retry 3 --silent --show-error \
|
||||||
|
--output "${RUNNER_TEMP}/${archive}" \
|
||||||
|
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${archive}"
|
||||||
|
echo "${ACTIONLINT_SHA256} ${RUNNER_TEMP}/${archive}" | sha256sum --check
|
||||||
|
tar -xzf "${RUNNER_TEMP}/${archive}" -C "${RUNNER_TEMP}" actionlint
|
||||||
|
"${RUNNER_TEMP}/actionlint" .github/workflows/*.yml
|
||||||
|
|
||||||
|
pre-commit:
|
||||||
|
name: Pre-commit
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
cache: pip
|
||||||
|
|
||||||
|
- name: Update setuptools
|
||||||
|
run: |
|
||||||
|
pip install -U setuptools wheel
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
pip install -q -e reme_studio -e ".[dev,core]"
|
||||||
|
pip install -q --no-deps -e plugins/auto-fin -e plugins/daily_paper -e plugins/lme -e plugins/beam
|
||||||
|
|
||||||
|
- name: Pre-commit starts
|
||||||
|
run: pre-commit run --all-files
|
||||||
|
|
@ -1,30 +1,36 @@
|
||||||
name: Tests ReMe
|
name: CI / Python tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, master, dev, develop]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, master, dev, develop]
|
branches: [main]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
unit-tests:
|
unit-tests:
|
||||||
name: Unit Tests - py${{ matrix.python-version }}
|
name: Unit Tests - py${{ matrix.python-version }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 90
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.11", "3.12", "3.13"]
|
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
|
@ -32,12 +38,15 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip setuptools wheel
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
pip install -e packages/reme_ai_studio -e ".[dev,core]"
|
pip install -e reme_studio -e ".[dev,core]"
|
||||||
|
pip install --no-deps -e plugins/auto-fin
|
||||||
|
pip install -e plugins/daily_paper
|
||||||
|
pip install -e plugins/lme -e plugins/beam
|
||||||
pip install coverage
|
pip install coverage
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: |
|
run: |
|
||||||
coverage run -m pytest tests/unit \
|
coverage run -m pytest tests/unit plugins/auto-fin plugins/daily_paper plugins/lme plugins/beam \
|
||||||
-v \
|
-v \
|
||||||
--tb=long \
|
--tb=long \
|
||||||
-s \
|
-s \
|
||||||
93
.github/workflows/ci-reme-studio.yml
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
name: CI / ReMe Studio
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "reme_studio/**"
|
||||||
|
- ".github/workflows/ci-reme-studio.yml"
|
||||||
|
- ".github/workflows/release-reme-studio.yml"
|
||||||
|
- "scripts/package_studio.py"
|
||||||
|
- "tests/unit/test_package_versions.py"
|
||||||
|
- "pyproject.toml"
|
||||||
|
- "LICENSE"
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "reme_studio/**"
|
||||||
|
- ".github/workflows/ci-reme-studio.yml"
|
||||||
|
- ".github/workflows/release-reme-studio.yml"
|
||||||
|
- "scripts/package_studio.py"
|
||||||
|
- "tests/unit/test_package_versions.py"
|
||||||
|
- "pyproject.toml"
|
||||||
|
- "LICENSE"
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
studio:
|
||||||
|
name: Studio checks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: reme_studio
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "22.22.3"
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: reme_studio/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run format check
|
||||||
|
run: npm run format:check
|
||||||
|
|
||||||
|
- name: Run lint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm test
|
||||||
|
|
||||||
|
- name: Verify npm package
|
||||||
|
run: |
|
||||||
|
npm pack --pack-destination "${RUNNER_TEMP}"
|
||||||
|
tar -tzf "${RUNNER_TEMP}"/agentscope-ai-reme_studio-*.tgz | grep '^package/dist-static/index.html$'
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Build and verify Python package
|
||||||
|
working-directory: .
|
||||||
|
run: |
|
||||||
|
python -m pip install build packaging pytest twine
|
||||||
|
PYTHONPATH=. python -m pytest tests/unit/test_package_versions.py -q
|
||||||
|
python scripts/package_studio.py
|
||||||
|
python -m build reme_studio --outdir dist/studio
|
||||||
|
python -m twine check dist/studio/*
|
||||||
|
STUDIO_WHEEL="$(pwd)/$(ls dist/studio/reme_studio-*.whl)"
|
||||||
|
python -m venv "${RUNNER_TEMP}/reme-studio-package-smoke"
|
||||||
|
"${RUNNER_TEMP}/reme-studio-package-smoke/bin/python" -m pip install "${STUDIO_WHEEL}"
|
||||||
|
cd "${RUNNER_TEMP}"
|
||||||
|
"${RUNNER_TEMP}/reme-studio-package-smoke/bin/python" - <<'PY'
|
||||||
|
from reme_studio import static_dir
|
||||||
|
|
||||||
|
assert (static_dir() / "index.html").is_file()
|
||||||
|
PY
|
||||||
|
|
@ -1,37 +1,36 @@
|
||||||
name: Windows Smoke
|
name: CI / Windows
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, master, dev, develop]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, master, dev, develop]
|
branches: [main]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cli-smoke:
|
cli-smoke:
|
||||||
name: CLI smoke - py${{ matrix.python-version }}
|
name: CLI smoke - py${{ matrix.python-version }}
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
timeout-minutes: 30
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.11"]
|
python-version: ["3.11"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Set up Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
persist-credentials: false
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: website/package-lock.json
|
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
|
@ -39,28 +38,15 @@ jobs:
|
||||||
- name: Install package
|
- name: Install package
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip setuptools wheel
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
pip install -e packages/reme_ai_studio -e ".[dev,core]"
|
pip install -e ".[dev,as]"
|
||||||
|
|
||||||
- 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
|
- name: Run version job
|
||||||
run: reme start service.backend=cli job=version
|
run: reme start config=tests/fixtures/config/version-smoke.yaml job=version
|
||||||
|
|
||||||
- name: Run Windows path tests
|
- name: Run Windows path tests
|
||||||
run: |
|
run: |
|
||||||
python -m pytest `
|
python -m pytest `
|
||||||
tests/unit/test_auto_dream.py::test_scan_day_files_includes_nested_md_and_excludes_interests `
|
tests/unit/test_auto_dream.py::test_scan_day_files_includes_only_markdown_day_files `
|
||||||
tests/unit/test_auto_dream.py::test_dream_extract_matches_posix_catalog_paths `
|
tests/unit/test_auto_dream.py::test_dream_extract_matches_posix_catalog_paths `
|
||||||
tests/unit/test_read_with_neighbors.py::test_read_with_neighbors_uses_posix_nested_path `
|
tests/unit/test_read_with_neighbors.py::test_read_with_neighbors_uses_posix_nested_path `
|
||||||
-v
|
-v
|
||||||
60
.github/workflows/deploy-docs.yml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
name: Deploy / Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "github-pages/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "reme/config/default.yaml"
|
||||||
|
- "integrations/claude_code/README.md"
|
||||||
|
- "integrations/hermes_agent/README.md"
|
||||||
|
- "integrations/hermes_agent/figures/**"
|
||||||
|
- "README.md"
|
||||||
|
- "README_ZH.md"
|
||||||
|
- "reme_studio/**"
|
||||||
|
- "integrations/dsh/README*.md"
|
||||||
|
- "integrations/dsh/figures/**"
|
||||||
|
- "integrations/openclaw/README*.md"
|
||||||
|
- "integrations/openclaw/figures/**"
|
||||||
|
- "plugins/*/README*.md"
|
||||||
|
- "benchmark/*/README*.md"
|
||||||
|
- "benchmark/toolmemory/gitcha.png"
|
||||||
|
- "AGENTS.md"
|
||||||
|
- ".github/workflows/deploy-docs.yml"
|
||||||
|
- ".github/workflows/_build-docs.yml"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pages
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build documentation
|
||||||
|
uses: ./.github/workflows/_build-docs.yml
|
||||||
|
with:
|
||||||
|
run_tests: true
|
||||||
|
upload_pages_artifact: true
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
timeout-minutes: 10
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Deploy
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
||||||
221
.github/workflows/docker.yml
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
name: CI and Release / Docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/docker.yml'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- '.dockerignore'
|
||||||
|
- 'docker-compose.yml'
|
||||||
|
- 'deploy/docker/**'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'README.md'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'reme/**'
|
||||||
|
- 'reme_studio/**'
|
||||||
|
- 'scripts/package_studio.py'
|
||||||
|
- 'scripts/test_docker_image.py'
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/docker.yml'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- '.dockerignore'
|
||||||
|
- 'docker-compose.yml'
|
||||||
|
- 'deploy/docker/**'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'README.md'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'reme/**'
|
||||||
|
- 'reme_studio/**'
|
||||||
|
- 'scripts/package_studio.py'
|
||||||
|
- 'scripts/test_docker_image.py'
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
PUBLISH_IMAGE: ${{ github.event_name == 'release' || (github.event_name != 'pull_request' && github.ref == 'refs/heads/main') }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and test / ${{ matrix.arch }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
timeout-minutes: 60
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- arch: amd64
|
||||||
|
runner: ubuntu-24.04
|
||||||
|
- arch: arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Validate package version
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ github.event.release.tag_name }}
|
||||||
|
run: |
|
||||||
|
python -m pip install packaging
|
||||||
|
if [ -n "$RELEASE_VERSION" ]; then
|
||||||
|
python scripts/bump_version.py --check --expected-version "$RELEASE_VERSION"
|
||||||
|
else
|
||||||
|
python scripts/bump_version.py --check
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Normalize image name
|
||||||
|
id: image
|
||||||
|
env:
|
||||||
|
REPOSITORY: ${{ github.repository }}
|
||||||
|
run: echo "name=ghcr.io/${REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Validate Compose
|
||||||
|
run: docker compose config --quiet
|
||||||
|
|
||||||
|
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4
|
||||||
|
|
||||||
|
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
|
||||||
|
id: metadata
|
||||||
|
with:
|
||||||
|
images: ${{ steps.image.outputs.name }}
|
||||||
|
flavor: latest=${{ github.event_name == 'release' && !github.event.release.prerelease && 'auto' || 'false' }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
type=pep440,pattern={{version}},value=${{ github.event.release.tag_name }},enable=${{ github.event_name == 'release' }}
|
||||||
|
type=sha
|
||||||
|
|
||||||
|
- name: Build local image
|
||||||
|
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/${{ matrix.arch }}
|
||||||
|
load: true
|
||||||
|
tags: reme:smoke
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
cache-from: type=gha,scope=reme-${{ matrix.arch }}
|
||||||
|
cache-to: type=gha,mode=max,scope=reme-${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Test installed image and persistent workspace
|
||||||
|
run: python scripts/test_docker_image.py --image reme:smoke
|
||||||
|
|
||||||
|
- name: Log in to GHCR
|
||||||
|
if: env.PUBLISH_IMAGE == 'true'
|
||||||
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Publish tested architecture by digest
|
||||||
|
id: publish
|
||||||
|
if: env.PUBLISH_IMAGE == 'true'
|
||||||
|
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/${{ matrix.arch }}
|
||||||
|
outputs: type=image,name=${{ steps.image.outputs.name }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
cache-from: type=gha,scope=reme-${{ matrix.arch }}
|
||||||
|
provenance: mode=max
|
||||||
|
sbom: true
|
||||||
|
|
||||||
|
- name: Record image digest
|
||||||
|
if: env.PUBLISH_IMAGE == 'true'
|
||||||
|
env:
|
||||||
|
IMAGE_DIGEST: ${{ steps.publish.outputs.digest }}
|
||||||
|
run: |
|
||||||
|
mkdir -p "$RUNNER_TEMP/digests"
|
||||||
|
touch "$RUNNER_TEMP/digests/${IMAGE_DIGEST#sha256:}"
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: env.PUBLISH_IMAGE == 'true'
|
||||||
|
with:
|
||||||
|
name: docker-digest-${{ matrix.arch }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
manifest:
|
||||||
|
name: Publish multi-platform tags
|
||||||
|
needs: build
|
||||||
|
if: github.event_name == 'release' || (github.event_name != 'pull_request' && github.ref == 'refs/heads/main')
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 15
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
pattern: docker-digest-*
|
||||||
|
merge-multiple: true
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
|
||||||
|
- name: Normalize image name
|
||||||
|
id: image
|
||||||
|
env:
|
||||||
|
REPOSITORY: ${{ github.repository }}
|
||||||
|
run: echo "name=ghcr.io/${REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4
|
||||||
|
|
||||||
|
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
|
||||||
|
id: metadata
|
||||||
|
with:
|
||||||
|
images: ${{ steps.image.outputs.name }}
|
||||||
|
flavor: latest=${{ github.event_name == 'release' && !github.event.release.prerelease && 'auto' || 'false' }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
type=pep440,pattern={{version}},value=${{ github.event.release.tag_name }},enable=${{ github.event_name == 'release' }}
|
||||||
|
type=sha
|
||||||
|
|
||||||
|
- name: Assemble and verify tags
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ${{ steps.image.outputs.name }}
|
||||||
|
IMAGE_TAGS: ${{ steps.metadata.outputs.tags }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
image_refs=()
|
||||||
|
for digest_file in "$RUNNER_TEMP"/digests/*; do
|
||||||
|
image_refs+=("${IMAGE_NAME}@sha256:$(basename "$digest_file")")
|
||||||
|
done
|
||||||
|
[ "${#image_refs[@]}" -eq 2 ]
|
||||||
|
tag_args=()
|
||||||
|
while IFS= read -r tag; do
|
||||||
|
tag_args+=(--tag "$tag")
|
||||||
|
done <<< "$IMAGE_TAGS"
|
||||||
|
docker buildx imagetools create "${tag_args[@]}" "${image_refs[@]}"
|
||||||
|
while IFS= read -r tag; do
|
||||||
|
manifest=$(docker buildx imagetools inspect "$tag" --raw)
|
||||||
|
IMAGE_MANIFEST="$manifest" python - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
manifest = json.loads(os.environ["IMAGE_MANIFEST"])
|
||||||
|
platforms = {(item["platform"]["os"], item["platform"]["architecture"]) for item in manifest["manifests"]}
|
||||||
|
assert {("linux", "amd64"), ("linux", "arm64")} <= platforms, platforms
|
||||||
|
PY
|
||||||
|
done <<< "$IMAGE_TAGS"
|
||||||
61
.github/workflows/github-pages-check.yml
vendored
|
|
@ -1,61 +0,0 @@
|
||||||
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
|
|
||||||
41
.github/workflows/npm-format.yml
vendored
|
|
@ -1,41 +0,0 @@
|
||||||
name: NPM Format
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- "website/**"
|
|
||||||
- ".github/workflows/npm-format.yml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "website/**"
|
|
||||||
- ".github/workflows/npm-format.yml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
website:
|
|
||||||
name: Website checks
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: website
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "22"
|
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: website/package-lock.json
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Run format check
|
|
||||||
run: npm run format:check
|
|
||||||
|
|
||||||
- name: Run lint
|
|
||||||
run: npm run lint
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: npm test
|
|
||||||
97
.github/workflows/package-check.yml
vendored
|
|
@ -1,97 +0,0 @@
|
||||||
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()"
|
|
||||||
68
.github/workflows/pages.yml
vendored
|
|
@ -1,68 +0,0 @@
|
||||||
name: Deploy ReMe documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- "github-pages/**"
|
|
||||||
- "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"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: pages
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: "22.13"
|
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: github-pages/package-lock.json
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
working-directory: github-pages
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build documentation
|
|
||||||
working-directory: github-pages
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Configure Pages
|
|
||||||
uses: actions/configure-pages@v5
|
|
||||||
|
|
||||||
- name: Upload Pages artifact
|
|
||||||
uses: actions/upload-pages-artifact@v4
|
|
||||||
with:
|
|
||||||
path: github-pages/dist
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
steps:
|
|
||||||
- name: Deploy
|
|
||||||
id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
name: PR Title Check
|
name: Policy / PR title
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, master, dev, develop]
|
branches: [main]
|
||||||
types: [opened, edited, synchronize, reopened]
|
types: [opened, edited, reopened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-pr-title:
|
check-pr-title:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- name: Check PR title format
|
- name: Check PR title format
|
||||||
uses: amannn/action-semantic-pull-request@v6.1.1
|
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
38
.github/workflows/pre-commit.yml
vendored
|
|
@ -1,38 +0,0 @@
|
||||||
name: Pre-commit
|
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: True
|
|
||||||
matrix:
|
|
||||||
os: [ ubuntu-latest ]
|
|
||||||
env:
|
|
||||||
OS: ${{ matrix.os }}
|
|
||||||
PYTHON: '3.11'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.11'
|
|
||||||
- name: Update setuptools
|
|
||||||
run: |
|
|
||||||
pip install -U setuptools wheel
|
|
||||||
- name: Install
|
|
||||||
run: |
|
|
||||||
pip install -q -e packages/reme_ai_studio -e ".[dev,core]"
|
|
||||||
- name: Install pre-commit
|
|
||||||
run: |
|
|
||||||
pre-commit install
|
|
||||||
- name: Pre-commit starts
|
|
||||||
run: |
|
|
||||||
pre-commit run --all-files > pre-commit.log 2>&1 || true
|
|
||||||
cat pre-commit.log
|
|
||||||
if grep -q Failed pre-commit.log; then
|
|
||||||
echo -e "\e[41m [**FAIL**] Please install pre-commit and format your code first. \e[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo -e "\e[46m ********************************Passed******************************** \e[0m"
|
|
||||||
123
.github/workflows/python-publish.yml
vendored
|
|
@ -1,123 +0,0 @@
|
||||||
name: Publish Python packages to PyPI
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: Release version
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
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.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
|
|
||||||
178
.github/workflows/release-auto-fin.yml
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
# 发布操作手册:
|
||||||
|
# 1. 先将 plugins/auto-fin/pyproject.toml 中的 project.version 更新为待发布版本并合入目标分支。
|
||||||
|
# 2. 确认插件依赖的 reme-ai 版本已经发布到 PyPI;本工作流会在构建阶段验证该依赖可下载。
|
||||||
|
# 3. 确认 PyPI Trusted Publisher 已绑定本仓库、此工作流和 pypi environment,且 PyPI 上不存在相同版本。
|
||||||
|
# 4. 在 GitHub 仓库的 Actions 页面选择“Release / Auto Fin plugin”,点击“Run workflow”。
|
||||||
|
# 5. 输入与 project.version 完全一致的版本号(例如 0.1.0)后运行;版本也可以带 v 前缀。
|
||||||
|
#
|
||||||
|
# 推荐发布顺序:reme-ai -> reme-auto-fin -> QwenPaw 更新依赖并通过 plugins: [auto-fin] 启用。
|
||||||
|
# 当前仅支持 workflow_dispatch 手动触发,不会因 push、tag 或 release 自动发布。
|
||||||
|
|
||||||
|
name: Release / Auto Fin plugin
|
||||||
|
|
||||||
|
run-name: Publish reme-auto-fin ${{ inputs.version }}
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Version from plugins/auto-fin/pyproject.toml (for example, 0.1.0)
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-reme-auto-fin
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ inputs.version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install test and build dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install build packaging pytest pytest-asyncio twine
|
||||||
|
python -m pip install -e ".[core]"
|
||||||
|
python -m pip install --no-deps -e plugins/auto-fin
|
||||||
|
|
||||||
|
- name: Validate package name and release version
|
||||||
|
id: package
|
||||||
|
run: |
|
||||||
|
python - "${RELEASE_VERSION}" <<'PY'
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from packaging.requirements import Requirement
|
||||||
|
from packaging.version import Version
|
||||||
|
|
||||||
|
project = tomllib.loads(Path("plugins/auto-fin/pyproject.toml").read_text(encoding="utf-8"))["project"]
|
||||||
|
expected = Version(sys.argv[1].removeprefix("v"))
|
||||||
|
actual = Version(project["version"])
|
||||||
|
if project["name"] != "reme-auto-fin":
|
||||||
|
raise SystemExit(f"Expected project name 'reme-auto-fin', found {project['name']!r}")
|
||||||
|
if actual != expected:
|
||||||
|
raise SystemExit(f"Package version is {actual}, but workflow input is {expected}")
|
||||||
|
requirements = [requirement for requirement in project["dependencies"] if requirement.startswith("reme-ai")]
|
||||||
|
if len(requirements) != 1:
|
||||||
|
raise SystemExit(f"Expected one reme-ai dependency, found {requirements!r}")
|
||||||
|
reme_requirement = Requirement(requirements[0])
|
||||||
|
if reme_requirement.name != "reme-ai" or reme_requirement.extras:
|
||||||
|
raise SystemExit(f"Expected a base reme-ai dependency, found {requirements[0]!r}")
|
||||||
|
if Version("0.4.1.11") in reme_requirement.specifier or Version("0.4.1.12") not in reme_requirement.specifier:
|
||||||
|
raise SystemExit(f"Expected reme-ai>=0.4.1.12, found {requirements[0]!r}")
|
||||||
|
root_project = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]
|
||||||
|
agentscope_requirements = [
|
||||||
|
Requirement(value) for value in root_project["optional-dependencies"]["as"]
|
||||||
|
]
|
||||||
|
if len(agentscope_requirements) != 1 or agentscope_requirements[0].name != "agentscope":
|
||||||
|
raise SystemExit(f"Expected one agentscope dependency, found {agentscope_requirements!r}")
|
||||||
|
agentscope_requirement = agentscope_requirements[0]
|
||||||
|
agentscope_specifiers = list(agentscope_requirement.specifier)
|
||||||
|
if (
|
||||||
|
agentscope_requirement.extras != {"model-ollama"}
|
||||||
|
or agentscope_requirement.marker is not None
|
||||||
|
or len(agentscope_specifiers) != 1
|
||||||
|
or agentscope_specifiers[0].operator != "=="
|
||||||
|
or Version(agentscope_specifiers[0].version).is_prerelease
|
||||||
|
):
|
||||||
|
raise SystemExit(f"Expected a stable exact agentscope[model-ollama] pin, found {agentscope_requirement}")
|
||||||
|
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
|
||||||
|
print(f"reme_requirement={reme_requirement}", file=output)
|
||||||
|
print(f"agentscope_requirement={agentscope_requirement}", file=output)
|
||||||
|
print(f"Publishing {project['name']} {actual}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Run Auto Fin tests
|
||||||
|
run: python -m pytest plugins/auto-fin -q
|
||||||
|
|
||||||
|
- name: Require the plugin-enabled ReMe release on PyPI
|
||||||
|
env:
|
||||||
|
REME_REQUIREMENT: ${{ steps.package.outputs.reme_requirement }}
|
||||||
|
run: |
|
||||||
|
python -m pip download --no-deps \
|
||||||
|
--dest "${RUNNER_TEMP}/reme-auto-fin-base" \
|
||||||
|
"${REME_REQUIREMENT}"
|
||||||
|
|
||||||
|
- name: Build and check distributions
|
||||||
|
run: |
|
||||||
|
mkdir -p dist/auto-fin
|
||||||
|
python -m build plugins/auto-fin --outdir dist/auto-fin
|
||||||
|
python -m twine check dist/auto-fin/*
|
||||||
|
|
||||||
|
- name: Verify distributions and isolated installation
|
||||||
|
env:
|
||||||
|
AGENTSCOPE_REQUIREMENT: ${{ steps.package.outputs.agentscope_requirement }}
|
||||||
|
run: |
|
||||||
|
AUTO_FIN_WHEEL="$(pwd)/$(ls dist/auto-fin/reme_auto_fin-*.whl)"
|
||||||
|
AUTO_FIN_SDIST="$(pwd)/$(ls dist/auto-fin/reme_auto_fin-*.tar.gz)"
|
||||||
|
python -m zipfile -l "${AUTO_FIN_WHEEL}" | grep 'dist-info/licenses/LICENSE'
|
||||||
|
python -m tarfile -l "${AUTO_FIN_SDIST}" | grep '/LICENSE'
|
||||||
|
python -m venv "${RUNNER_TEMP}/reme-auto-fin-smoke"
|
||||||
|
"${RUNNER_TEMP}/reme-auto-fin-smoke/bin/python" -m pip install \
|
||||||
|
"${AGENTSCOPE_REQUIREMENT}" "${AUTO_FIN_WHEEL}"
|
||||||
|
cd "${RUNNER_TEMP}"
|
||||||
|
"${RUNNER_TEMP}/reme-auto-fin-smoke/bin/python" - <<'PY'
|
||||||
|
from importlib.metadata import distribution
|
||||||
|
|
||||||
|
from reme.plugin_manifest import load_package_manifest
|
||||||
|
|
||||||
|
package = distribution("reme-auto-fin")
|
||||||
|
plugins = {entry.name: entry for entry in package.entry_points if entry.group == "reme.plugins"}
|
||||||
|
assert plugins["auto-fin"].value == "reme_auto_fin"
|
||||||
|
manifest = load_package_manifest("reme_auto_fin", plugin_name="auto-fin")
|
||||||
|
assert set(manifest.backends) == {
|
||||||
|
"auto_fin_data_step",
|
||||||
|
"auto_fin_topic_step",
|
||||||
|
"auto_fin_merge_step",
|
||||||
|
}
|
||||||
|
assert set(manifest.application_defaults["jobs"]) == {
|
||||||
|
"auto_fin",
|
||||||
|
"auto_fin_cron",
|
||||||
|
}
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Upload distributions
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: reme-auto-fin-${{ inputs.version }}
|
||||||
|
path: dist/auto-fin/
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: pypi
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download distributions
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: reme-auto-fin-${{ inputs.version }}
|
||||||
|
path: dist/auto-fin
|
||||||
|
|
||||||
|
- name: Publish reme-auto-fin
|
||||||
|
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
||||||
|
with:
|
||||||
|
packages-dir: dist/auto-fin
|
||||||
178
.github/workflows/release-daily-paper.yml
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
# Release checklist:
|
||||||
|
# 1. Update project.version in plugins/daily_paper/pyproject.toml and merge it into the target branch.
|
||||||
|
# 2. Publish the required reme-ai version before this plugin; the build verifies that dependency on PyPI.
|
||||||
|
# 3. Configure PyPI Trusted Publishing for this repository/workflow and its pypi environment.
|
||||||
|
# 4. Run "Release / Daily Paper plugin" from GitHub Actions with the exact project version (a v prefix is accepted).
|
||||||
|
#
|
||||||
|
# Recommended order: reme-ai -> reme-daily-paper -> downstream applications enabling plugins: [daily-paper].
|
||||||
|
# This workflow is intentionally manual and never publishes from a push, tag, or GitHub release event.
|
||||||
|
|
||||||
|
name: Release / Daily Paper plugin
|
||||||
|
|
||||||
|
run-name: Publish reme-daily-paper ${{ inputs.version }}
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Version from plugins/daily_paper/pyproject.toml (for example, 0.1.0)
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-reme-daily-paper
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ inputs.version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install test and build dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install build packaging pytest pytest-asyncio twine
|
||||||
|
python -m pip install -e ".[core]"
|
||||||
|
python -m pip install -e plugins/daily_paper
|
||||||
|
|
||||||
|
- name: Validate package name, dependencies, and release version
|
||||||
|
id: package
|
||||||
|
run: |
|
||||||
|
python - "${RELEASE_VERSION}" <<'PY'
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from packaging.requirements import Requirement
|
||||||
|
from packaging.version import Version
|
||||||
|
|
||||||
|
project = tomllib.loads(Path("plugins/daily_paper/pyproject.toml").read_text(encoding="utf-8"))["project"]
|
||||||
|
expected = Version(sys.argv[1].removeprefix("v"))
|
||||||
|
actual = Version(project["version"])
|
||||||
|
if project["name"] != "reme-daily-paper":
|
||||||
|
raise SystemExit(f"Expected project name 'reme-daily-paper', found {project['name']!r}")
|
||||||
|
if actual != expected:
|
||||||
|
raise SystemExit(f"Package version is {actual}, but workflow input is {expected}")
|
||||||
|
requirements = [Requirement(value) for value in project["dependencies"]]
|
||||||
|
reme_requirements = [requirement for requirement in requirements if requirement.name == "reme-ai"]
|
||||||
|
if len(reme_requirements) != 1 or reme_requirements[0].extras:
|
||||||
|
raise SystemExit(f"Expected one base reme-ai dependency, found {reme_requirements!r}")
|
||||||
|
if Version("0.4.1.11") in reme_requirements[0].specifier or Version("0.4.1.12") not in reme_requirements[0].specifier:
|
||||||
|
raise SystemExit(f"Expected reme-ai>=0.4.1.12, found {reme_requirements!r}")
|
||||||
|
if sum(requirement.name == "pypdf" for requirement in requirements) != 1:
|
||||||
|
raise SystemExit("Expected exactly one pypdf dependency")
|
||||||
|
root_project = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]
|
||||||
|
agentscope_requirements = [
|
||||||
|
Requirement(value) for value in root_project["optional-dependencies"]["as"]
|
||||||
|
]
|
||||||
|
if len(agentscope_requirements) != 1 or agentscope_requirements[0].name != "agentscope":
|
||||||
|
raise SystemExit(f"Expected one agentscope dependency, found {agentscope_requirements!r}")
|
||||||
|
agentscope_requirement = agentscope_requirements[0]
|
||||||
|
agentscope_specifiers = list(agentscope_requirement.specifier)
|
||||||
|
if (
|
||||||
|
agentscope_requirement.extras != {"model-ollama"}
|
||||||
|
or agentscope_requirement.marker is not None
|
||||||
|
or len(agentscope_specifiers) != 1
|
||||||
|
or agentscope_specifiers[0].operator != "=="
|
||||||
|
or Version(agentscope_specifiers[0].version).is_prerelease
|
||||||
|
):
|
||||||
|
raise SystemExit(f"Expected a stable exact agentscope[model-ollama] pin, found {agentscope_requirement}")
|
||||||
|
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
|
||||||
|
print(f"reme_requirement={reme_requirements[0]}", file=output)
|
||||||
|
print(f"agentscope_requirement={agentscope_requirement}", file=output)
|
||||||
|
print(f"Publishing {project['name']} {actual}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Run Daily Paper tests
|
||||||
|
run: python -m pytest plugins/daily_paper -q
|
||||||
|
|
||||||
|
- name: Require the plugin-enabled ReMe release on PyPI
|
||||||
|
env:
|
||||||
|
REME_REQUIREMENT: ${{ steps.package.outputs.reme_requirement }}
|
||||||
|
run: |
|
||||||
|
python -m pip download --no-deps \
|
||||||
|
--dest "${RUNNER_TEMP}/reme-daily-paper-base" \
|
||||||
|
"${REME_REQUIREMENT}"
|
||||||
|
|
||||||
|
- name: Build and check distributions
|
||||||
|
run: |
|
||||||
|
mkdir -p dist/daily-paper
|
||||||
|
python -m build plugins/daily_paper --outdir dist/daily-paper
|
||||||
|
python -m twine check dist/daily-paper/*
|
||||||
|
|
||||||
|
- name: Verify distributions and isolated installation
|
||||||
|
env:
|
||||||
|
AGENTSCOPE_REQUIREMENT: ${{ steps.package.outputs.agentscope_requirement }}
|
||||||
|
run: |
|
||||||
|
DAILY_PAPER_WHEEL="$(pwd)/$(ls dist/daily-paper/reme_daily_paper-*.whl)"
|
||||||
|
DAILY_PAPER_SDIST="$(pwd)/$(ls dist/daily-paper/reme_daily_paper-*.tar.gz)"
|
||||||
|
python -m zipfile -l "${DAILY_PAPER_WHEEL}" | grep 'reme_daily_paper/plugin.yaml'
|
||||||
|
python -m zipfile -l "${DAILY_PAPER_WHEEL}" | grep 'reme_daily_paper/analyze.yaml'
|
||||||
|
python -m zipfile -l "${DAILY_PAPER_WHEEL}" | grep 'dist-info/licenses/LICENSE'
|
||||||
|
python -m tarfile -l "${DAILY_PAPER_SDIST}" | grep '/LICENSE'
|
||||||
|
python -m venv "${RUNNER_TEMP}/reme-daily-paper-smoke"
|
||||||
|
"${RUNNER_TEMP}/reme-daily-paper-smoke/bin/python" -m pip install \
|
||||||
|
"${AGENTSCOPE_REQUIREMENT}" "${DAILY_PAPER_WHEEL}"
|
||||||
|
cd "${RUNNER_TEMP}"
|
||||||
|
"${RUNNER_TEMP}/reme-daily-paper-smoke/bin/python" - <<'PY'
|
||||||
|
from importlib.metadata import distribution
|
||||||
|
|
||||||
|
from reme.plugin_manifest import load_package_manifest
|
||||||
|
|
||||||
|
package = distribution("reme-daily-paper")
|
||||||
|
plugins = {entry.name: entry for entry in package.entry_points if entry.group == "reme.plugins"}
|
||||||
|
assert plugins["daily-paper"].value == "reme_daily_paper"
|
||||||
|
manifest = load_package_manifest("reme_daily_paper", plugin_name="daily-paper")
|
||||||
|
assert set(manifest.backends) == {
|
||||||
|
"daily_paper_collect_step",
|
||||||
|
"daily_paper_rank_step",
|
||||||
|
"daily_paper_select_step",
|
||||||
|
"daily_paper_analyze_step",
|
||||||
|
"daily_paper_digest_step",
|
||||||
|
}
|
||||||
|
assert set(manifest.application_defaults["jobs"]) == {"daily_paper", "daily_paper_cron"}
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Upload distributions
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: reme-daily-paper-${{ inputs.version }}
|
||||||
|
path: dist/daily-paper/
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: pypi
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download distributions
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: reme-daily-paper-${{ inputs.version }}
|
||||||
|
path: dist/daily-paper
|
||||||
|
|
||||||
|
- name: Publish reme-daily-paper
|
||||||
|
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
||||||
|
with:
|
||||||
|
packages-dir: dist/daily-paper
|
||||||
49
.github/workflows/release-dsh-plugin.yml
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Configure npm Trusted Publishing for this caller filename and the npm environment.
|
||||||
|
name: Release / DSH plugin
|
||||||
|
|
||||||
|
run-name: Publish ReMe DSH plugin ${{ inputs.version }} (${{ inputs.npm_tag }})
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Exact package.json version; an optional v prefix is accepted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
npm_tag:
|
||||||
|
description: npm distribution tag
|
||||||
|
required: true
|
||||||
|
default: latest
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- next
|
||||||
|
- latest
|
||||||
|
use_npm_token:
|
||||||
|
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-reme-dsh-plugin
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
uses: ./.github/workflows/_release-npm-plugin.yml
|
||||||
|
secrets:
|
||||||
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
with:
|
||||||
|
directory: integrations/dsh
|
||||||
|
package_name: '@agentscope-ai/reme-dsh-plugin'
|
||||||
|
artifact_name: agentscope-ai-reme-dsh-plugin
|
||||||
|
version: ${{ inputs.version }}
|
||||||
|
npm_tag: ${{ inputs.npm_tag }}
|
||||||
|
use_npm_token: ${{ inputs.use_npm_token }}
|
||||||
75
.github/workflows/release-openclaw-plugin.yml
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Configure npm Trusted Publishing for this caller filename and the npm environment.
|
||||||
|
name: Release / OpenClaw plugin
|
||||||
|
|
||||||
|
run-name: Publish ReMe OpenClaw plugin ${{ inputs.version }} (${{ inputs.npm_tag }})
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Exact package.json version; an optional v prefix is accepted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
npm_tag:
|
||||||
|
description: npm distribution tag
|
||||||
|
required: true
|
||||||
|
default: latest
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- next
|
||||||
|
- latest
|
||||||
|
use_npm_token:
|
||||||
|
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
publish_clawhub:
|
||||||
|
description: Also publish the package to ClawHub
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-reme-openclaw-plugin
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
uses: ./.github/workflows/_release-npm-plugin.yml
|
||||||
|
secrets:
|
||||||
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
with:
|
||||||
|
directory: integrations/openclaw
|
||||||
|
package_name: '@agentscope-ai/reme-openclaw-plugin'
|
||||||
|
artifact_name: agentscope-ai-reme-openclaw-plugin
|
||||||
|
version: ${{ inputs.version }}
|
||||||
|
npm_tag: ${{ inputs.npm_tag }}
|
||||||
|
use_npm_token: ${{ inputs.use_npm_token }}
|
||||||
|
validate_clawhub: true
|
||||||
|
|
||||||
|
publish-clawhub:
|
||||||
|
if: ${{ inputs.publish_clawhub && github.ref == 'refs/heads/main' }}
|
||||||
|
needs: release
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
uses: openclaw/clawhub/.github/workflows/package-publish.yml@cacf5ec1b0ee3cb532ab4555a68c1db9e0c1aac7 # v0.24.0
|
||||||
|
with:
|
||||||
|
family: code-plugin
|
||||||
|
version: ${{ needs.release.outputs.version }}
|
||||||
|
tags: ${{ inputs.npm_tag }}
|
||||||
|
source_repo: ${{ github.repository }}
|
||||||
|
source_commit: ${{ github.sha }}
|
||||||
|
source_ref: ${{ github.sha }}
|
||||||
|
source_path: integrations/openclaw
|
||||||
|
package_artifact_name: agentscope-ai-reme-openclaw-plugin-${{ needs.release.outputs.version }}
|
||||||
|
dry_run: false
|
||||||
|
wait_for_publication: true
|
||||||
52
.github/workflows/release-python.yml
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
name: Release / ReMe Python package
|
||||||
|
|
||||||
|
# Publishing a GitHub Release is the primary release trigger for reme-ai; workflow_dispatch is the recovery path.
|
||||||
|
# Configure a PyPI Trusted Publisher for this repository, workflow, and its pypi environment first.
|
||||||
|
|
||||||
|
run-name: Publish reme-ai ${{ github.event.release.tag_name || inputs.version }}
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Exact reme-ai version; an optional v prefix is accepted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-reme-ai
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and verify distributions
|
||||||
|
uses: ./.github/workflows/_build-python-packages.yml
|
||||||
|
with:
|
||||||
|
expected_version: ${{ github.event.release.tag_name || inputs.version }}
|
||||||
|
upload_artifacts: true
|
||||||
|
|
||||||
|
publish-reme:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: pypi
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Download ReMe distributions
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: reme-distributions
|
||||||
|
path: dist/reme
|
||||||
|
|
||||||
|
- name: Publish ReMe
|
||||||
|
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
||||||
|
with:
|
||||||
|
packages-dir: dist/reme
|
||||||
|
skip-existing: true
|
||||||
243
.github/workflows/release-reme-studio.yml
vendored
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
# Release checklist:
|
||||||
|
# 1. Update reme_studio/pyproject.toml, package.json, and package-lock.json to the same Studio version.
|
||||||
|
# 2. Configure npm Trusted Publishing with the npm environment and PyPI Trusted Publishing with the pypi environment.
|
||||||
|
# 3. Run this workflow manually with the exact Studio version.
|
||||||
|
|
||||||
|
name: Release / ReMe Studio
|
||||||
|
|
||||||
|
run-name: Publish ReMe Studio ${{ inputs.version }} (${{ inputs.npm_tag }})
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Version from the Studio Python and npm manifests
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
npm_tag:
|
||||||
|
description: npm distribution tag
|
||||||
|
required: true
|
||||||
|
default: latest
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- next
|
||||||
|
- latest
|
||||||
|
use_npm_token:
|
||||||
|
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
publish_target:
|
||||||
|
description: Packages to publish; single-package modes are for release recovery
|
||||||
|
required: true
|
||||||
|
default: both
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- both
|
||||||
|
- pypi
|
||||||
|
- npm
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: publish-reme-studio
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ inputs.version }}
|
||||||
|
NPM_TAG: ${{ inputs.npm_tag }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "22.22.3"
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: reme_studio/package-lock.json
|
||||||
|
|
||||||
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Validate Studio package names and version
|
||||||
|
run: |
|
||||||
|
python - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
studio = Path("reme_studio")
|
||||||
|
python_manifest = tomllib.loads((studio / "pyproject.toml").read_text(encoding="utf-8"))["project"]
|
||||||
|
npm_manifest = json.loads((studio / "package.json").read_text(encoding="utf-8"))
|
||||||
|
expected = os.environ["RELEASE_VERSION"].removeprefix("v")
|
||||||
|
if python_manifest["name"] != "reme_studio":
|
||||||
|
raise SystemExit(f"Unexpected Python package name: {python_manifest['name']}")
|
||||||
|
if npm_manifest["name"] != "@agentscope-ai/reme_studio":
|
||||||
|
raise SystemExit(f"Unexpected npm package name: {npm_manifest['name']}")
|
||||||
|
if python_manifest["version"] != expected or npm_manifest["version"] != expected:
|
||||||
|
raise SystemExit(
|
||||||
|
f"Studio manifests are {python_manifest['version']} and {npm_manifest['version']}; "
|
||||||
|
f"workflow input is {expected}",
|
||||||
|
)
|
||||||
|
prerelease = "-" in expected
|
||||||
|
if prerelease != (os.environ["NPM_TAG"] == "next"):
|
||||||
|
raise SystemExit("Prereleases must use next; stable releases must use latest")
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Install dependencies and run checks
|
||||||
|
working-directory: reme_studio
|
||||||
|
run: |
|
||||||
|
npm ci
|
||||||
|
npm run format:check
|
||||||
|
npm run lint
|
||||||
|
npm test
|
||||||
|
|
||||||
|
- name: Build Studio distributions
|
||||||
|
run: |
|
||||||
|
python -m pip install build twine
|
||||||
|
mkdir -p dist/studio-python dist/studio-npm
|
||||||
|
npm pack ./reme_studio --pack-destination dist/studio-npm
|
||||||
|
python scripts/package_studio.py
|
||||||
|
python -m build reme_studio --outdir dist/studio-python
|
||||||
|
python -m twine check dist/studio-python/*
|
||||||
|
|
||||||
|
- name: Verify Studio distributions and isolated installation
|
||||||
|
run: |
|
||||||
|
STUDIO_WHEEL="$(pwd)/$(ls dist/studio-python/reme_studio-*.whl)"
|
||||||
|
tar -tzf dist/studio-npm/*.tgz | grep '^package/dist-static/index.html$'
|
||||||
|
python -m venv "${RUNNER_TEMP}/reme-studio-package-smoke"
|
||||||
|
"${RUNNER_TEMP}/reme-studio-package-smoke/bin/python" -m pip install "${STUDIO_WHEEL}"
|
||||||
|
cd "${RUNNER_TEMP}"
|
||||||
|
"${RUNNER_TEMP}/reme-studio-package-smoke/bin/python" - <<'PY'
|
||||||
|
from reme_studio import static_dir
|
||||||
|
|
||||||
|
assert (static_dir() / "index.html").is_file()
|
||||||
|
PY
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: reme-studio-${{ inputs.version }}
|
||||||
|
path: |
|
||||||
|
dist/studio-python/*
|
||||||
|
dist/studio-npm/*
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
publish-python:
|
||||||
|
if: inputs.publish_target != 'npm'
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: pypi
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: reme-studio-${{ inputs.version }}
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
- name: Publish ReMe Studio to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
||||||
|
with:
|
||||||
|
packages-dir: dist/studio-python
|
||||||
|
skip-existing: true
|
||||||
|
|
||||||
|
publish-npm:
|
||||||
|
if: >-
|
||||||
|
!cancelled() &&
|
||||||
|
needs.build.result == 'success' &&
|
||||||
|
(inputs.publish_target == 'npm' ||
|
||||||
|
(inputs.publish_target == 'both' && needs.publish-python.result == 'success'))
|
||||||
|
needs: [build, publish-python]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: npm
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||||
|
with:
|
||||||
|
node-version: "24"
|
||||||
|
registry-url: https://registry.npmjs.org
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: reme-studio-${{ inputs.version }}
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
- name: Verify the matching PyPI release for npm-only recovery
|
||||||
|
if: inputs.publish_target == 'npm'
|
||||||
|
env:
|
||||||
|
PACKAGE_VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
python - <<'PY'
|
||||||
|
import os
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
version = os.environ["PACKAGE_VERSION"].removeprefix("v")
|
||||||
|
url = f"https://pypi.org/pypi/reme-studio/{version}/json"
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(url, timeout=30) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
raise SystemExit(f"Unexpected PyPI response for reme-studio {version}: {response.status}")
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
raise SystemExit(f"reme-studio {version} must exist on PyPI before npm-only recovery") from exc
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Check for an identical existing npm package
|
||||||
|
id: npm-version
|
||||||
|
env:
|
||||||
|
PACKAGE_VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
package_file=$(find dist/studio-npm -maxdepth 1 -name '*.tgz' -print -quit)
|
||||||
|
if [[ -z "${package_file}" ]]; then
|
||||||
|
echo "Studio npm artifact is missing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
local_integrity=$(node --input-type=module - "${package_file}" <<'JS'
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
const digest = createHash('sha512').update(readFileSync(process.argv[2])).digest('base64');
|
||||||
|
console.log(`sha512-${digest}`);
|
||||||
|
JS
|
||||||
|
)
|
||||||
|
if remote_integrity=$(npm view "@agentscope-ai/reme_studio@${PACKAGE_VERSION#v}" dist.integrity 2>/dev/null); then
|
||||||
|
if [[ "${remote_integrity}" != "${local_integrity}" ]]; then
|
||||||
|
echo "Existing npm package has different contents" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "exists=true" >> "${GITHUB_OUTPUT}"
|
||||||
|
echo "The identical npm package already exists; nothing to publish"
|
||||||
|
else
|
||||||
|
echo "exists=false" >> "${GITHUB_OUTPUT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Publish ReMe Studio to npm with Trusted Publishing
|
||||||
|
if: ${{ steps.npm-version.outputs.exists != 'true' && !inputs.use_npm_token }}
|
||||||
|
env:
|
||||||
|
NPM_TAG: ${{ inputs.npm_tag }}
|
||||||
|
run: npm publish dist/studio-npm/*.tgz --access public --tag "${NPM_TAG}" --provenance
|
||||||
|
- name: Publish ReMe Studio to npm with NPM_TOKEN
|
||||||
|
if: ${{ steps.npm-version.outputs.exists != 'true' && inputs.use_npm_token }}
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
NPM_TAG: ${{ inputs.npm_tag }}
|
||||||
|
run: |
|
||||||
|
if [[ -z "${NODE_AUTH_TOKEN}" ]]; then
|
||||||
|
echo "NPM_TOKEN is required when use_npm_token is enabled" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
npm publish dist/studio-npm/*.tgz --access public --tag "${NPM_TAG}" --provenance
|
||||||
45
.github/workflows/security-codeql.yml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
name: Security / CodeQL
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 1 * * 1'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze ${{ matrix.language }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [python, javascript-typescript]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
build-mode: none
|
||||||
|
|
||||||
|
- name: Perform CodeQL analysis
|
||||||
|
uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
|
||||||
|
with:
|
||||||
|
category: /language:${{ matrix.language }}
|
||||||
6
.gitignore
vendored
|
|
@ -30,11 +30,9 @@ htmlcov/
|
||||||
# Packaging / build outputs
|
# Packaging / build outputs
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
node_modules/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
|
integrations/*/reports/
|
||||||
# Website build integration source (not generated output)
|
|
||||||
!website/build/
|
|
||||||
!website/build/**
|
|
||||||
|
|
||||||
# Logs / temporary files
|
# Logs / temporary files
|
||||||
*.log
|
*.log
|
||||||
|
|
|
||||||
27
AGENTS.md
|
|
@ -41,30 +41,35 @@ and concise documentation together.
|
||||||
- `reme/components/application_context.py`: application-wide wiring and in-memory shared state.
|
- `reme/components/application_context.py`: application-wide wiring and in-memory shared state.
|
||||||
- `reme/components/runtime_context.py`: request-scoped data, response, streaming queue, and stop event.
|
- `reme/components/runtime_context.py`: request-scoped data, response, streaming queue, and stop event.
|
||||||
- `reme/components/base_component.py`: component lifecycle, dependency binding, and workspace helpers.
|
- `reme/components/base_component.py`: component lifecycle, dependency binding, and workspace helpers.
|
||||||
- `reme/components/component_registry.py`: the process-wide `(component type, backend)` registry.
|
- `reme/components/component_registry.py`: the frozen built-in registry template and application-local registry factory.
|
||||||
- `reme/components/job/`: base, stream, background, and cron job implementations.
|
- `reme/components/job/`: base, stream, background, and cron job implementations.
|
||||||
- `reme/components/service/`: local CLI, HTTP, and MCP service backends.
|
- `reme/components/service/`: local CLI, HTTP, and MCP service backends.
|
||||||
- `reme/components/`: agent wrappers, model adapters, stores, catalogs, graphs, indexes, clients, tokenizers, and
|
- `reme/components/`: agent wrappers, model adapters, stores, catalogs, graphs, indexes, clients, tokenizers, and
|
||||||
outbound proxies.
|
outbound proxies.
|
||||||
- `reme/steps/`: registered job steps grouped by common, file I/O, index, evolve, cookbook, benchmark, and transfer
|
- `reme/steps/`: registered job steps grouped by common, file I/O, index, evolve, cookbook, and transfer
|
||||||
concerns.
|
concerns.
|
||||||
- `reme/utils/`: shared utilities, including service discovery, logging, web-static resolution, session I/O, token
|
- `reme/utils/`: shared utilities, including service discovery, logging, web-static resolution, session I/O, token
|
||||||
accounting, and wikilink handling.
|
accounting, and wikilink handling.
|
||||||
- `tests/unit/`: primary fast, isolated validation suite.
|
- `tests/unit/`: primary fast, isolated validation suite.
|
||||||
- `tests/integration/`: service/model tests that may need credentials or external processes.
|
- `tests/integration/`: service/model tests that may need credentials or external processes.
|
||||||
- `website/`: ReMe Workspace frontend source; its static build can be served by the HTTP service.
|
- `reme_studio/`: ReMe Studio frontend source plus the independently published `reme_studio` Python package and
|
||||||
- `plugins/claude_code/` and `plugins/hermes_agent/`: agent integrations.
|
`@agentscope-ai/reme_studio` npm static distribution.
|
||||||
|
- `plugins/`: installable ReMe extensions, including Auto Fin and LME/BEAM plugins.
|
||||||
|
- `integrations/`: adapters that connect ReMe to external agent hosts, including the independent, self-contained DSH
|
||||||
|
and OpenClaw TypeScript plugins plus the Claude Code and Hermes Agent integrations.
|
||||||
- `skills/`: standalone skills; `reme_memory` calls ReMe, while other skills may use separate tools or direct-file
|
- `skills/`: standalone skills; `reme_memory` calls ReMe, while other skills may use separate tools or direct-file
|
||||||
conventions.
|
conventions.
|
||||||
- `benchmark/` and `cookbook/`: runnable evaluation and example workflows.
|
- `benchmark/` and `cookbook/`: runnable evaluations and example workflows.
|
||||||
- `docs/`: README-linked supporting pages and figures.
|
- `docs/`: README-linked supporting pages and figures.
|
||||||
|
- `github-pages/`: VitePress build shell, generated-content assembly, documentation checks, and GitHub Pages output. The
|
||||||
|
canonical theme and guides remain under `docs/`; `.generated/` and `dist/` are disposable.
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
ReMe requires Python 3.11 or newer. Install the editable development environment with:
|
ReMe requires Python 3.11 or newer. Install the editable development environment with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -e packages/reme_ai_studio -e ".[dev,core]"
|
pip install -e reme_studio -e ".[dev,core]"
|
||||||
```
|
```
|
||||||
|
|
||||||
Before changing behavior, inspect the adjacent implementation, schema, built-in config, and focused tests. Follow
|
Before changing behavior, inspect the adjacent implementation, schema, built-in config, and focused tests. Follow
|
||||||
|
|
@ -184,25 +189,29 @@ pre-commit run --all-files
|
||||||
```
|
```
|
||||||
|
|
||||||
Black and Flake8 use a 120-character line limit and Python 3.11 formatting; Pylint is also run by pre-commit. If
|
Black and Flake8 use a 120-character line limit and Python 3.11 formatting; Pylint is also run by pre-commit. If
|
||||||
`website/` changes, use its Node 22.13+ scripts and run the proportionate checks from that directory, such as
|
`reme_studio/` changes, use its Node 22.13+ scripts and run the proportionate checks from that directory, such as
|
||||||
`npm run format:check`, `npm run lint`, or `npm test`.
|
`npm run format:check`, `npm run lint`, or `npm test`.
|
||||||
|
|
||||||
Integration tests may contact real model providers, services, or agent subprocesses and can require credentials. Do not
|
Integration tests may contact real model providers, services, or agent subprocesses and can require credentials. Do not
|
||||||
run credentialed or externally mutating tests automatically; run them only when the task requires them and the necessary
|
run credentialed or externally mutating tests automatically; run them only when the task requires them and the necessary
|
||||||
environment has been supplied or authorized. Mock network, model, and subprocess boundaries in unit tests.
|
environment has been supplied or authorized. Mock network, model, and subprocess boundaries in unit tests.
|
||||||
|
|
||||||
|
If documentation or the documentation theme changes, run `npm test` and `npm run build` from `github-pages/`. The Job
|
||||||
|
reference is generated from `reme/config/default.yaml`; do not edit generated pages directly.
|
||||||
|
|
||||||
## Change Guardrails
|
## Change Guardrails
|
||||||
|
|
||||||
- Preserve unrelated user changes in a dirty working tree.
|
- Preserve unrelated user changes in a dirty working tree.
|
||||||
- Make the smallest coherent change and avoid unrelated cleanup or broad refactors.
|
- Make the smallest coherent change and avoid unrelated cleanup or broad refactors.
|
||||||
- Do not edit generated output when the source can be changed instead. The publish workflow builds
|
- Do not edit generated output when the source can be changed instead. The publish workflow builds
|
||||||
`website/dist-static` and copies it into `reme/web`; change `website/` source for frontend work.
|
`reme_studio/dist-static` and stages it under `reme_studio/src/reme_studio/static`; change `reme_studio/` source for
|
||||||
|
frontend work.
|
||||||
- Do not silently change CLI flags, configuration keys, workspace layouts, serialized schemas, endpoint shapes,
|
- Do not silently change CLI flags, configuration keys, workspace layouts, serialized schemas, endpoint shapes,
|
||||||
streaming termination, or service interfaces. Preserve compatibility where practical and document intentional
|
streaming termination, or service interfaces. Preserve compatibility where practical and document intentional
|
||||||
migrations.
|
migrations.
|
||||||
- Do not introduce dependencies without a concrete repository-level need.
|
- Do not introduce dependencies without a concrete repository-level need.
|
||||||
- Do not commit `.env` files, credentials, runtime memory, logs, indexes, caches, benchmark outputs, or generated
|
- Do not commit `.env` files, credentials, runtime memory, logs, indexes, caches, benchmark outputs, or generated
|
||||||
website distributions.
|
Studio distributions.
|
||||||
- State which validations passed and which relevant checks were not run in the final handoff.
|
- State which validations passed and which relevant checks were not run in the final handoff.
|
||||||
|
|
||||||
If a requirement is ambiguous, infer intent from nearby code, schemas, defaults, and tests. Ask the user only when the
|
If a requirement is ambiguous, infer intent from nearby code, schemas, defaults, and tests. Ask the user only when the
|
||||||
|
|
|
||||||
54
Dockerfile
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM node:22-bookworm-slim AS studio-builder
|
||||||
|
WORKDIR /build/reme_studio
|
||||||
|
COPY reme_studio/package.json reme_studio/package-lock.json ./
|
||||||
|
RUN --mount=type=cache,target=/root/.npm npm ci
|
||||||
|
COPY reme_studio/ ./
|
||||||
|
RUN npm run build:static && test -f dist-static/index.html
|
||||||
|
|
||||||
|
FROM python:3.11-slim-bookworm AS python-builder
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends build-essential \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /build
|
||||||
|
COPY pyproject.toml README.md LICENSE ./
|
||||||
|
COPY reme/ reme/
|
||||||
|
COPY reme_studio/pyproject.toml reme_studio/README.md reme_studio/LICENSE reme_studio/
|
||||||
|
COPY reme_studio/src/ reme_studio/src/
|
||||||
|
COPY --from=studio-builder /build/reme_studio/dist-static/ reme_studio/dist-static/
|
||||||
|
COPY scripts/package_studio.py scripts/package_studio.py
|
||||||
|
RUN python scripts/package_studio.py && python -m venv /opt/venv
|
||||||
|
ENV PATH="/opt/venv/bin:${PATH}"
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
|
python -m pip install --upgrade pip \
|
||||||
|
&& python -m pip install ./reme_studio ".[core,image-heif]" \
|
||||||
|
&& python -m pip check
|
||||||
|
# Check installed resources away from the checkout, so source files cannot mask
|
||||||
|
# incomplete wheels or a Studio package fetched accidentally from PyPI.
|
||||||
|
WORKDIR /tmp
|
||||||
|
RUN python -I -c "import reme; from reme_studio import static_dir; from reme.config import resolve_app_config; assert (static_dir() / 'index.html').is_file(); assert resolve_app_config(log_config=False)['service']['backend'] == 'http'"
|
||||||
|
|
||||||
|
FROM python:3.11-slim-bookworm AS runtime
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends ca-certificates git libgomp1 libstdc++6 tini tzdata \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& groupadd --gid 1000 reme \
|
||||||
|
&& useradd --uid 1000 --gid reme --no-create-home reme \
|
||||||
|
&& mkdir -p /app /data \
|
||||||
|
&& chown reme:reme /app /data
|
||||||
|
COPY --from=python-builder /opt/venv /opt/venv
|
||||||
|
COPY deploy/docker/reme_container.py /usr/local/lib/reme_container.py
|
||||||
|
ENV PATH="/opt/venv/bin:${PATH}" \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
HOME=/tmp/reme-home \
|
||||||
|
REME_WORKSPACE_DIR=/data \
|
||||||
|
REME_HOST=0.0.0.0
|
||||||
|
WORKDIR /app
|
||||||
|
USER reme
|
||||||
|
EXPOSE 2333
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=120s --retries=3 \
|
||||||
|
CMD ["python", "/usr/local/lib/reme_container.py", "--healthcheck"]
|
||||||
|
ENTRYPOINT ["/usr/bin/tini", "--", "python", "/usr/local/lib/reme_container.py"]
|
||||||
|
CMD ["start"]
|
||||||
309
README.md
|
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="docs/figure/reme_logo.png" alt="ReMe Logo" width="50%">
|
<img src="https://raw.githubusercontent.com/agentscope-ai/ReMe/main/docs/figure/reme_logo.png" alt="ReMe Logo" width="50%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
@ -27,51 +27,72 @@
|
||||||
> [0.2.x](https://github.com/agentscope-ai/ReMe/tree/v0.2.0.6) ·
|
> [0.2.x](https://github.com/agentscope-ai/ReMe/tree/v0.2.0.6) ·
|
||||||
> [MemoryScope](https://github.com/agentscope-ai/ReMe/tree/memoryscope_branch)
|
> [MemoryScope](https://github.com/agentscope-ai/ReMe/tree/memoryscope_branch)
|
||||||
|
|
||||||
🧠 ReMe turns conversations and resources into readable, editable, searchable, and interconnected Markdown memory. It
|
## ✨ Why ReMe?
|
||||||
works alongside agents such as QwenPaw, OpenClaw, Hermes, and Claude Code, continuously organizing what they learn while
|
|
||||||
keeping the files under the user's control.
|
|
||||||
|
|
||||||
## ✨ Core Ideas
|
🧠 ReMe turns conversations and resources into readable, editable, searchable, and interconnected Markdown memory. Agents
|
||||||
|
such as QwenPaw and DeepSeek Harness can share the same workspace to retrieve, maintain, and evolve knowledge, while
|
||||||
|
users retain control of the durable files.
|
||||||
|
|
||||||
- **Memory as File, File as Memory**: Markdown files with frontmatter and wikilinks serve as memory nodes that both
|
- **Memory as File, File as Memory**: ReMe stores durable memory as ordinary Markdown with frontmatter and wikilinks.
|
||||||
users and agents can inspect, edit, move, and back up directly.
|
Users and agents can inspect, edit, move, sync, and back it up with familiar tools, while indexes and generated
|
||||||
- **Self-evolving knowledge base**: Auto Memory, Auto Resource, and Auto Dream progressively transform conversations and
|
metadata remain rebuildable.
|
||||||
resources into daily notes and long-term knowledge, while Auto Link writes relationships and sources back into the
|
- **Self-evolving knowledge base**: ReMe progressively turns conversations and resources into daily notes and long-term
|
||||||
files.
|
knowledge, preserving sources while refining facts, preferences, procedures, and relationships over time.
|
||||||
- **Progressive hybrid search**: ReMe combines wikilinks, BM25, and embeddings for hybrid retrieval across keyword
|
- **Recall is precise and context-aware.** BM25, optional embeddings, and wikilink expansion retrieve relevant
|
||||||
matching, optional semantic recall, and relationship expansion without loading every neighboring file into context.
|
line-level passages and their relationships without loading the entire knowledge base into the agent context.
|
||||||
- **Agent-friendly integration**: SKILL.md + CLI integration makes it easy for different agents to read, write,
|
- **One memory workspace works across agents.** Personal assistants, coding agents, and other agent runtimes can share
|
||||||
maintain, and reuse the same local workspace. HTTP, MCP, and Python integrations are also available.
|
the same local workspace through native integrations, SKILL.md, CLI, HTTP, MCP, or Python APIs.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="docs/figure/design-philosophy.svg" alt="ReMe Design Philosophy" width="92%">
|
<img src="docs/figure/design-philosophy.svg" alt="ReMe Design Philosophy" width="92%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 🔭 Use Cases
|
## 📰 Latest Updates
|
||||||
|
|
||||||
- **Personal assistants**: Give personal assistants such as
|
- [2026.10] - **[ReMe Studio Playground](https://reme.agentscope.io/studio/?lang=en) is live**: explore example memory
|
||||||
[QwenPaw](https://github.com/agentscope-ai/QwenPaw), [OpenClaw](https://github.com/openclaw/openclaw), and
|
files, edit Markdown, and browse linked memory graphs right in your browser—no installation or backend required.
|
||||||
[Hermes](https://github.com/nousresearch/hermes-agent) a user-editable long-term memory layer.
|
Everyone is welcome to [try it out](https://reme.agentscope.io/studio/?lang=en)!
|
||||||
- **Coding agents**: Preserve coding style, project background, repository decisions, and workflow experience across
|
|
||||||
sessions when integrating with coding agents such as [Claude Code](plugins/claude_code/reme).
|
|
||||||
- **LLM Wiki**: Turn conversations, notes, and resources into a searchable, traceable, and linked Markdown knowledge
|
|
||||||
base that both users and agents can maintain.
|
|
||||||
- **Self-evolving agents**: Support agents that learn from experience by saving successful paths, failed attempts,
|
|
||||||
reusable procedures, and periodic reflections as memory.
|
|
||||||
|
|
||||||
## 📰 News
|
<p align="center">
|
||||||
|
<a href="https://reme.agentscope.io/studio/?lang=en">
|
||||||
|
<img src="https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/studio-overview.png" alt="ReMe Studio workspace preview — click to try the Playground" width="480" style="margin: 0 auto;">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
- [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
|
- [2026.09] - **[ReMe Memory Tags](https://reme.agentscope.io/en/blog_20260920) published**: an introduction
|
||||||
architecture, self-evolving workflows, hybrid search, proactive discovery, and benchmark results.
|
to file-native entity tags, rebuildable tag indexes, and tag-filtered memory search.
|
||||||
- [2026.08] - [Experience-driven enhancement method](https://reme.agentscope.io/?doc=toolmemory-en) of agent tool-use execution built
|
- [2026.09] - **[Hermes Agent memory provider](https://reme.agentscope.io/en/integrations/hermes) available**: choose HTTP or embedded
|
||||||
on ReMe is available on [arXiv:2608.03403](https://arxiv.org/abs/2608.03403).
|
mode for automatic recall before model calls and asynchronous `auto_memory` after completed turns. The integration
|
||||||
- [2026.07] - Introduced optional Cookbooks: [Daily Paper](https://reme.agentscope.io/?doc=daily-paper-en) for paper discovery and
|
supports Hermes Agent 0.21+ and includes profile-aware background work.
|
||||||
analysis, and [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-en) for researching the latest 24 hours of topic-related CLS news
|
- [2026.09] - **[OpenClaw plugin](https://reme.agentscope.io/en/integrations/openclaw) released**: install it from
|
||||||
with local-memory search and validated historical wikilinks.
|
[ClawHub](https://clawhub.ai/agentscope-ai/plugins/reme-openclaw-plugin) or
|
||||||
|
[npm](https://www.npmjs.com/package/@agentscope-ai/reme-openclaw-plugin) to add native memory recall, automatic
|
||||||
|
conversation capture, and scheduled consolidation to OpenClaw.
|
||||||
|
- [2026.09] - **[DeepSeek Harness plugin](https://reme.agentscope.io/en/integrations/dsh) released**: install it from
|
||||||
|
[Awesome DSH Plugin](https://awesome-dsh-plugin.com/p/agentscope-ai/ReMe--integrations-dsh/) or
|
||||||
|
[npm](https://www.npmjs.com/package/@agentscope-ai/reme-dsh-plugin) for long-term-memory guidance, `reme_search`,
|
||||||
|
automatic memory, Auto Dream, and ReMe Status.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>More updates</summary>
|
||||||
|
|
||||||
|
- [2026.08] - **ReMe blog published**: the [ReMe blog](https://reme.agentscope.io/en/reme-blog) introduces the
|
||||||
|
local-first memory architecture, self-evolving workflows, hybrid search, proactive discovery, and benchmark results.
|
||||||
|
- [2026.08] - **New ReMe ecosystem plugins**: [Daily Paper](https://reme.agentscope.io/en/plugins/daily-paper)
|
||||||
|
discovers and analyzes papers and generates file-native briefs, while
|
||||||
|
[Auto Fin](https://reme.agentscope.io/en/plugins/auto-fin) researches the latest 24 hours of topic-related CLS news
|
||||||
|
and builds traceable reports with local memory. Try them out.
|
||||||
|
- [2026.08] - **Plugin development support released**: use [Plugin Development](https://reme.agentscope.io/en/plugin_development) and
|
||||||
|
[Plugin Management](https://reme.agentscope.io/en/plugin_management) to extend ReMe with Components, Steps, and Jobs. Contributions and
|
||||||
|
new community plugins are welcome.
|
||||||
|
- [2026.08] - ReMe's [experience-driven enhancement method](https://reme.agentscope.io/en/benchmarks/toolmemory) for
|
||||||
|
agent tool use is available on [arXiv:2608.03403](https://arxiv.org/abs/2608.03403).
|
||||||
- [2026.07] - Our
|
- [2026.07] - Our
|
||||||
paper [Remember Me, Refine Me: A Dynamic Procedural Memory Framework for Experience-Driven Agent Evolution](https://aclanthology.org/2026.findings-acl.829/)
|
paper [Remember Me, Refine Me: A Dynamic Procedural Memory Framework for Experience-Driven Agent Evolution](https://aclanthology.org/2026.findings-acl.829/)
|
||||||
has been accepted to Findings of ACL 2026.
|
has been accepted to Findings of ACL 2026.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
@ -89,8 +110,8 @@ Install from source:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/agentscope-ai/ReMe.git
|
git clone https://github.com/agentscope-ai/ReMe.git
|
||||||
cd ReMe
|
cd ReMe
|
||||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
pip install -e reme_studio -e ".[core]"
|
||||||
cd website
|
cd reme_studio
|
||||||
npm ci
|
npm ci
|
||||||
npm run build:static
|
npm run build:static
|
||||||
cd ..
|
cd ..
|
||||||
|
|
@ -98,6 +119,19 @@ cd ..
|
||||||
|
|
||||||
The static build requires Node.js 22.13 or newer and makes Studio available from the source tree.
|
The static build requires Node.js 22.13 or newer and makes Studio available from the source tree.
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
With Docker and Compose 2.24.0+, build and start ReMe with the bundled Studio:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .reme
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Open <http://127.0.0.1:2333>. The complete workspace persists in `./.reme`. On Linux, set `REME_UID` and `REME_GID` to your
|
||||||
|
user's IDs when they differ from 1000. See [Docker deployment](https://reme.agentscope.io/en/docker) for model credentials,
|
||||||
|
custom paths, published images, and upgrades.
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Configure environment variables when you want LLM-powered memory evolution or embedding retrieval. Embeddings are
|
Configure environment variables when you want LLM-powered memory evolution or embedding retrieval. Embeddings are
|
||||||
|
|
@ -109,7 +143,7 @@ cat > .env <<'EOF'
|
||||||
# EMBEDDING_API_KEY=sk-xxx
|
# EMBEDDING_API_KEY=sk-xxx
|
||||||
# EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
# EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
|
|
||||||
# Required for auto_memory, auto_resource, and auto_dream.
|
# Required for auto_memory, auto_resource, auto_dream, and proactive refresh.
|
||||||
LLM_API_KEY=sk-xxx
|
LLM_API_KEY=sk-xxx
|
||||||
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -121,7 +155,7 @@ Basic file operations, BM25 search, wikilink traversal, and reading proactive to
|
||||||
> To enable embedding-based semantic retrieval, uncomment `components.as_embedding` and
|
> To enable embedding-based semantic retrieval, uncomment `components.as_embedding` and
|
||||||
> `components.embedding_store` in [`reme/config/default.yaml`](reme/config/default.yaml), then change
|
> `components.embedding_store` in [`reme/config/default.yaml`](reme/config/default.yaml), then change
|
||||||
> `components.file_store.default.embedding_store` from `""` to `default`. See the
|
> `components.file_store.default.embedding_store` from `""` to `default`. See the
|
||||||
> [memory search guide](docs/en/memory_search.md) for details.
|
> [memory search guide](https://reme.agentscope.io/en/memory_search) for details.
|
||||||
|
|
||||||
### Start the Service
|
### Start the Service
|
||||||
|
|
||||||
|
|
@ -143,12 +177,6 @@ reme help
|
||||||
curl -s http://127.0.0.1:2333/version -H 'Content-Type: application/json' -d '{}'
|
curl -s http://127.0.0.1:2333/version -H 'Content-Type: application/json' -d '{}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### ReMe Studio (Optional)
|
|
||||||
|
|
||||||
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
|
### 5-Minute Memory Demo
|
||||||
|
|
||||||
With the service running, write a memory node, let ReMe index it, then retrieve it:
|
With the service running, write a memory node, let ReMe index it, then retrieve it:
|
||||||
|
|
@ -183,35 +211,56 @@ ReMe stores agent memory as readable Markdown.
|
||||||
Related: [[digest/wiki/memory-as-file.md]]
|
Related: [[digest/wiki/memory-as-file.md]]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📚 Usage Guides
|
### ReMe Studio (Optional)
|
||||||
|
|
||||||
These Markdown guides cover the main user workflows and the runtime contracts implemented by the current code.
|
The `core` installation 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/en/workspace/studio) for source builds, configuration, and development.
|
||||||
|
|
||||||
| Guide | What you will learn |
|
## 🤝 Use ReMe with Your Agent
|
||||||
|-------|---------------------|
|
|
||||||
| [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. |
|
|
||||||
| [Auto Dream](docs/en/auto_dream.md) and [Auto Link](docs/en/auto_link.md) | Consolidate daily notes into evolving digest nodes and readable wikilink relationships. |
|
|
||||||
| [Memory Search](docs/en/memory_search.md) | Use BM25, optional vectors, RRF fusion, line-range recall, and progressive link expansion. |
|
|
||||||
| [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 Blog](https://agentscope-ai.github.io/ReMe/?doc=en-reme-blog) | Read the product story, design rationale, examples, and benchmark summary. |
|
|
||||||
|
|
||||||
## 🧑🍳 Cookbooks
|
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. Host integrations can add memory guidance, recall, and capture to the agent
|
||||||
|
lifecycle according to the capabilities of each runtime.
|
||||||
|
|
||||||
Cookbooks are optional, end-to-end workflows assembled from ReMe jobs and steps. They are not enabled by the default
|
| Agent | Recommended path | Available after integration |
|
||||||
configuration; select the cookbook's standalone configuration when starting ReMe. Each new cookbook will be added as
|
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||||
another row in this table.
|
| **DeepSeek Harness** | Install [`@agentscope-ai/reme-dsh-plugin`](https://reme.agentscope.io/en/integrations/dsh) with `dsh plugin --profile web add @agentscope-ai/reme-dsh-plugin`. | Configurable memory guidance, `reme_search`, automatic turn capture, scheduled Auto Dream, and ReMe Status. |
|
||||||
|
| **OpenClaw** | Install [`@agentscope-ai/reme-openclaw-plugin`](https://reme.agentscope.io/en/integrations/openclaw) with `openclaw plugins install clawhub:@agentscope-ai/reme-openclaw-plugin`. | Native memory tools, recall before user-triggered runs, and automatic turn capture. |
|
||||||
|
| **QwenPaw** | Embed ReMe in-process through its Python API. | Reuse the host lifecycle and model config while keeping memory local and file-based. |
|
||||||
|
| **Claude Code** | Start the shared streamable HTTP MCP service and install [the ReMe plugin](https://reme.agentscope.io/en/integrations/claude-code). | Semantic, graph, and state recall through MCP, plus asynchronous session capture through a Stop hook. |
|
||||||
|
| **Hermes** | Install [the ReMe provider](https://reme.agentscope.io/en/integrations/hermes) and choose HTTP or embedded mode. | Recall before model calls and asynchronous `auto_memory` after each completed turn. |
|
||||||
|
| **Codex and other CLI agents** | Install or copy the [ReMe Memory skill](skills/reme_memory/SKILL.md). | Search, read, and write memory through the CLI; automatic capture requires host lifecycle integration. |
|
||||||
|
|
||||||
| Cookbook | Capability |
|
<p align="center"><b>Integration demos</b></p>
|
||||||
|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------|
|
|
||||||
| [Daily Paper](https://reme.agentscope.io/?doc=daily-paper-en) | Discover and rank papers, analyze PDFs with an agent, and generate file-native notes and a five-minute brief. |
|
|
||||||
| [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-en) | Fetch topic-related CLS news, search ReMe history, and generate wikilink-backed Markdown reports. |
|
|
||||||
|
|
||||||
## 📁 Memory System
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"></td>
|
||||||
|
<td width="45%" align="center"><b>Auto Memory</b></td>
|
||||||
|
<td width="45%" align="center"><b>Auto Dream</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><b>QwenPaw</b></td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/qwenpaw-auto-memory.gif" alt="QwenPaw Auto Memory demo" width="100%">
|
||||||
|
</td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/qwenpaw-auto-dream.gif" alt="QwenPaw Auto Dream demo" width="100%">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><b>Claude Code</b></td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/cc-auto-memory.gif" alt="Claude Code Auto Memory demo" width="100%">
|
||||||
|
</td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/cc-auto-dream.gif" alt="Claude Code Auto Dream demo" width="100%">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## 🧠 How ReMe Works
|
||||||
|
|
||||||
> Memory as File, File as Memory.
|
> Memory as File, File as Memory.
|
||||||
|
|
||||||
|
|
@ -219,7 +268,7 @@ ReMe treats **memory as files**, progressively processing filtered conversation
|
||||||
from `session/` and `resource/` into `daily/`, then `digest/`. The default workspace is `.reme/` under the current
|
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; `workspace_dir=...` selects a different user-owned location.
|
||||||
|
|
||||||
### Directory Structure
|
### Workspace Layout
|
||||||
|
|
||||||
```text
|
```text
|
||||||
<workspace_dir>/
|
<workspace_dir>/
|
||||||
|
|
@ -255,18 +304,18 @@ directory; `workspace_dir=...` selects a different user-owned location.
|
||||||
<img src="docs/figure/reme-overview.svg" alt="ReMe file-based memory system overview" width="92%">
|
<img src="docs/figure/reme-overview.svg" alt="ReMe file-based memory system overview" width="92%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 🧭 Memory Design Philosophy
|
### Memory Lifecycle
|
||||||
|
|
||||||
ReMe follows a capture → index → consolidate → recall loop. Workspace files remain the durable source of truth;
|
ReMe follows a capture → index → consolidate → recall loop. Workspace files remain the durable source of truth;
|
||||||
everything under `metadata/` is rebuildable.
|
everything under `metadata/` is rebuildable.
|
||||||
|
|
||||||
| Capability | Entry point | What it does | Output |
|
| Capability | Entry point | What it does | Output |
|
||||||
|---------------------------------------------|-------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
|
| ------------------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
||||||
| [`auto_memory`](docs/en/auto_memory.md) | Agent hook or `reme auto_memory` | Distills useful conversation facts while preserving a filtered conversation source record. | `session/dialog/*.jsonl`, `daily/<date>/<generated-name>.md` |
|
| [`auto_memory`](https://reme.agentscope.io/en/auto_memory) | Agent hook or `reme auto_memory` | Distills useful conversation facts while preserving a filtered conversation source record. | `session/dialog/*.jsonl`, `daily/<date>/<generated-name>.md` |
|
||||||
| [`auto_resource`](docs/en/auto_resource.md) | Resource watcher or `reme auto_resource` | Turns files under `resource/` into source-linked, content-named daily cards. | `daily/<date>/<resource-card>.md` |
|
| [`auto_resource`](https://reme.agentscope.io/en/auto_resource) | Resource watcher or `reme auto_resource` | Turns files under `resource/` into source-linked, content-named daily cards. | `daily/<date>/<resource-card>.md` |
|
||||||
| [`auto_index`](docs/en/memory_search.md) | Background watcher or `reme reindex` | Live-indexes Markdown in `daily/` and `digest/`; a full rebuild also scans `resource/` and JSONL. | Searchable chunks, BM25, wikilink graph, and optional vectors |
|
| [`auto_index`](https://reme.agentscope.io/en/memory_search) | Background watcher or `reme reindex` | The watcher ingests Markdown from `daily/` and `digest/`; `reindex` only rebuilds BM25 and embeddings from already-ingested chunks. | Searchable chunks, BM25, wikilink graph, and optional vectors |
|
||||||
| [`auto_dream`](docs/en/auto_dream.md) | `dream_cron` or `reme auto_dream` | By default, extracts up to five reusable units from changed files in the latest two-day window, then creates, corroborates, refines, or corrects digest nodes. | `digest/**`, `daily/<date>/interests.yaml` |
|
| [`auto_dream`](https://reme.agentscope.io/en/auto_dream) | `dream_cron` or `reme auto_dream` | By default, extracts up to five reusable units from changed files in the latest two-day window, then creates, corroborates, refines, or corrects digest nodes. | `digest/**` |
|
||||||
| [`proactive`](docs/en/proactive.md) | `reme proactive` before an agent decides to act | Reads topics generated by `auto_dream`; the host agent decides whether and how to mention them. | Structured topics from `daily/<date>/interests.yaml` |
|
| [`proactive_read`](https://reme.agentscope.io/en/proactive) | `reme proactive_read` before an agent decides to act | Reads topics generated by the independent proactive refresh flow; the host agent decides whether and how to mention them. | Structured topics from `daily/<date>/interests.yaml` |
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -291,88 +340,86 @@ Search returns matching chunks with line ranges and bounded wikilink neighbors.
|
||||||
BM25 through reciprocal rank fusion (RRF).
|
BM25 through reciprocal rank fusion (RRF).
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> `proactive` only reads and exposes interest topics produced by Auto Dream. It does not independently browse the web,
|
>
|
||||||
|
> `proactive_read` only reads and exposes interest topics produced by proactive refresh. It does not independently browse the web,
|
||||||
> send notifications, or rewrite the knowledge base; the host agent decides whether and how to act on a topic.
|
> send notifications, or rewrite the knowledge base; the host agent decides whether and how to act on a topic.
|
||||||
|
|
||||||
## 📊 Performance
|
## 📊 Benchmarks
|
||||||
|
|
||||||
ReMe evaluates multi-session and long-context memory with agentic search-and-read workflows. The figures below are the
|
ReMe evaluates multi-session and long-context memory with agentic search-and-read workflows. The figures below are the
|
||||||
published reference runs in this repository; model, prompt, dataset, and judging details are documented with each
|
published reference runs in this repository; model, prompt, dataset, and judging details are documented with each
|
||||||
benchmark.
|
benchmark.
|
||||||
|
|
||||||
| Benchmark | Setting | Sample size | Agentic score | Focus |
|
| Benchmark | Setting | Sample size | Agentic score | Focus |
|
||||||
|--------------------------------------------------------------|--------------|-------------------------:|--------------:|--------------------------------------------------------------------|
|
| --------------------------------------------------------------------------- | ------------ | -----------------------: | ------------: | ------------------------------------------------------------------ |
|
||||||
| **[LongMemEval cleaned-s](https://reme.agentscope.io/?doc=longmemeval-en)** | **Overall** | **500 questions** | **89.4%** | Cross-session retrieval, knowledge updates, and temporal reasoning |
|
| **[LongMemEval cleaned-s](https://reme.agentscope.io/en/benchmarks/longmemeval)** | **Overall** | **500 questions** | **89.4%** | Cross-session retrieval, knowledge updates, and temporal reasoning |
|
||||||
| [BEAM](https://reme.agentscope.io/?doc=beam-en) | 100K context | 20 cases / 400 questions | 66.1% | Ten types of long-context memory tasks |
|
| [BEAM](https://reme.agentscope.io/en/benchmarks/beam) | 100K context | 20 cases / 400 questions | 66.1% | Ten types of long-context memory tasks |
|
||||||
| [BEAM](https://reme.agentscope.io/?doc=beam-en) | 1M context | 35 cases / 700 questions | 65.0% | Ultra-long conversation settings |
|
| [BEAM](https://reme.agentscope.io/en/benchmarks/beam) | 1M context | 35 cases / 700 questions | 65.0% | Ultra-long conversation settings |
|
||||||
|
|
||||||
ReMe also achieved a **0.580 PROC score across five user personas** in the repository's
|
ReMe also achieved a **0.580 PROC score across five user personas** in the repository's
|
||||||
[π-Bench evaluation](https://reme.agentscope.io/?doc=pibench-en), 2.4% above NanoBot under the same test-model configuration. PROC
|
[π-Bench evaluation](https://reme.agentscope.io/en/benchmarks/pibench), 2.4% above NanoBot under the same test-model configuration. PROC
|
||||||
measures proactive handling of hidden intent, clarification, cross-session preferences and conventions, task
|
measures proactive handling of hidden intent, clarification, cross-session preferences and conventions, task
|
||||||
dependencies, and underspecified requests.
|
dependencies, and underspecified requests.
|
||||||
|
|
||||||
## 🤝 Agent-friendly Integration
|
## 🧩 Extensions and Plugins
|
||||||
|
|
||||||
ReMe can run as a local memory service accessed through the CLI, HTTP API, or MCP server, or it can be embedded in the
|
Plugins are optional Python distributions that contribute Component, Step, or Job backends and configuration. They are
|
||||||
host process through its Python API.
|
installed separately and enabled explicitly by configuration. Daily Paper and Auto Fin are independently packaged
|
||||||
|
plugins; see their documentation for [Daily Paper](https://reme.agentscope.io/en/plugins/daily-paper) and
|
||||||
|
[Auto Fin](https://reme.agentscope.io/en/plugins/auto-fin).
|
||||||
|
|
||||||
| Agents | Recommended path | Available after integration |
|
| Plugin | Capability |
|
||||||
|-----------------------------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||||
| **QwenPaw** | Embed ReMe in-process through its Python API. | Reuse the host application's lifecycle and model config while keeping memory local and file-based. |
|
| [Daily Paper](https://reme.agentscope.io/en/plugins/daily-paper) | Discover and rank papers, analyze PDFs with an agent, and generate file-native notes and a five-minute brief. |
|
||||||
| **Claude Code** | Start the streamable HTTP MCP service and install [plugins/claude_code/reme](plugins/claude_code/reme). | MCP recall tools, a `reme-memory` skill, and a Stop hook that records sessions automatically. |
|
| [Auto Fin](https://reme.agentscope.io/en/plugins/auto-fin) | Fetch topic-related CLS news, search ReMe history, and generate wikilink-backed Markdown reports. |
|
||||||
| **Hermes** | Start the HTTP service and install [plugins/hermes_agent](plugins/hermes_agent). | Recall relevant memory before model calls and enqueue `auto_memory` after each completed turn. |
|
|
||||||
| **Other CLI-capable agents (OpenClaw/Codex)** | Copy or install [skills/reme_memory/SKILL.md](skills/reme_memory/SKILL.md). | Search, read, and write memory via the CLI; automatic recording requires explicit host lifecycle hooks. |
|
|
||||||
|
|
||||||
<p align="center"><b>Integration demos</b></p>
|
See [Plugin Management](https://reme.agentscope.io/en/plugin_management) to install, inspect, validate, enable, and uninstall ReMe plugins.
|
||||||
|
|
||||||
<table>
|
## 📚 Documentation
|
||||||
<tr>
|
|
||||||
<td align="center"></td>
|
|
||||||
<td width="45%" align="center"><b>Auto Memory</b></td>
|
|
||||||
<td width="45%" align="center"><b>Auto Dream</b></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><b>QwenPaw</b></td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/qwenpaw-auto-memory.gif" alt="QwenPaw Auto Memory demo" width="100%">
|
|
||||||
</td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/qwenpaw-auto-dream.gif" alt="QwenPaw Auto Dream demo" width="100%">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><b>Claude Code</b></td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/cc-auto-memory.gif" alt="Claude Code Auto Memory demo" width="100%">
|
|
||||||
</td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/cc-auto-dream.gif" alt="Claude Code Auto Dream demo" width="100%">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
## 🛠️ ReMe Operations
|
These guides cover the main user workflows and the runtime contracts implemented by the current code.
|
||||||
|
|
||||||
|
| Guide | What you will learn |
|
||||||
|
| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| [Quick Start](https://reme.agentscope.io/en/quick_start) | Install ReMe, start the service, and run the first file and memory operations. |
|
||||||
|
| [Configuration](https://reme.agentscope.io/en/configuration) | Configure the workspace, models, Service, Jobs, Components, plugins, and CLI overrides. |
|
||||||
|
| [Services and Deployment](https://reme.agentscope.io/en/services) | Use HTTP, SSE, MCP, and Studio while respecting the default security boundary. |
|
||||||
|
| [Memory as File](https://reme.agentscope.io/en/memory_as_file) | Understand workspace layers, frontmatter, wikilinks, chunks, and the file-as-source-of-truth model. |
|
||||||
|
| [Auto Memory](https://reme.agentscope.io/en/auto_memory) | Preserve source conversations and distill reusable daily memory cards. |
|
||||||
|
| [Auto Resource](https://reme.agentscope.io/en/auto_resource) | Import supported text and image resources as source-linked daily cards. |
|
||||||
|
| [Auto Dream](https://reme.agentscope.io/en/auto_dream) and [Auto Link](https://reme.agentscope.io/en/auto_link) | Consolidate daily notes into evolving digest nodes and readable wikilink relationships. |
|
||||||
|
| [Memory Search](https://reme.agentscope.io/en/memory_search) | Use BM25, optional vectors, RRF fusion, line-range recall, and progressive link expansion. |
|
||||||
|
| [Proactive](https://reme.agentscope.io/en/proactive) | Read interest topics safely and integrate them into a host agent's decision flow. |
|
||||||
|
| [Application Scenarios](https://reme.agentscope.io/en/reme_scene) | Follow concrete financial research, coding-memory, and personal knowledge-base examples. |
|
||||||
|
| [Framework](https://reme.agentscope.io/en/framework) | Understand Application, Job, Step, Component, service, configuration, and lifecycle boundaries. |
|
||||||
|
| [Agent Integrations](https://reme.agentscope.io/en/integrations) | Choose an interface and connect DSH, Claude Code, OpenClaw, Hermes, Codex, or another agent. |
|
||||||
|
| [DSH plugin](https://reme.agentscope.io/en/integrations/dsh) and [Claude Code plugin](https://reme.agentscope.io/en/integrations/claude-code) | Configure host-native recall, automatic capture, consolidation, and diagnostics. |
|
||||||
|
| [CLI and Job API](https://reme.agentscope.io/en/reference/cli) | Learn command syntax and use the generated default Job parameter reference. |
|
||||||
|
| [Operations and Recovery](https://reme.agentscope.io/en/operations) | Diagnose services, maintain indexes, and back up, migrate, or recover a workspace. |
|
||||||
|
| [ReMe Blog](https://reme.agentscope.io/en/reme-blog) | Read the product story, design rationale, examples, and benchmark summary. |
|
||||||
|
|
||||||
|
## 🛠️ Common Commands
|
||||||
|
|
||||||
Run `reme help` for the full job list. Common workspace and maintenance commands are:
|
Run `reme help` for the full job list. Common workspace and maintenance commands are:
|
||||||
|
|
||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
|-------------------------------------------|----------------------------------------------------------------------------------------|
|
| ----------------------------------------- | --------------------------------------------------------------------------------- |
|
||||||
| `reme status` | Show stateful data-component memory estimates and process RSS. |
|
| `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 search`](https://reme.agentscope.io/en/memory_search) | 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 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 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 chat` | Stream a read-only, workspace-aware agent conversation. Requires LLM credentials. |
|
||||||
| `reme reindex` | Rebuild search and wikilink indexes from existing files. |
|
| `reme reindex` | Rebuild BM25 and embedding indexes from already-ingested chunks. |
|
||||||
|
|
||||||
## 🤝 Community and Support
|
## 🤝 Community and Contributing
|
||||||
|
|
||||||
- **Issues, requests, and help**: Check [Open Issues](https://github.com/agentscope-ai/ReMe/issues) first. If there is no
|
- **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.
|
related discussion, open one with the background, expected behavior, and impact scope.
|
||||||
- **Code contributions**: Before making changes, read
|
- **Code contributions**: Before making changes, read the repository's
|
||||||
the [contribution guide](https://docs.agentscope.io/reme/latest/en/contribution). Source, schemas, and tests are the
|
[contribution guide](https://reme.agentscope.io/en/contributing). Source, schemas, and tests are the authoritative architecture and
|
||||||
authoritative architecture and extension guide.
|
extension guide.
|
||||||
- **Documentation contributions**: Submit user-facing documentation changes to the
|
- **Documentation contributions**: Update the canonical files under `docs/en/`, `docs/zh/`, or the relevant package
|
||||||
[unified documentation repository](https://github.com/agentscope-ai/docs) under `reme/<version>/{en,zh}/`.
|
directory in this repository. The documentation site is generated from these files.
|
||||||
- **Commit convention**: Conventional Commits are recommended, for example `feat(search): add link expansion option` or
|
- **Commit convention**: Conventional Commits are recommended, for example `feat(search): add link expansion option` or
|
||||||
`docs(zh): update quick start`.
|
`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
|
- **Pre-submit checks**: Before submitting a PR, try to run `pre-commit run --all-files` and `pytest`. If tests that
|
||||||
|
|
|
||||||
281
README_ZH.md
|
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="docs/figure/reme_logo.png" alt="ReMe Logo" width="50%">
|
<img src="https://raw.githubusercontent.com/agentscope-ai/ReMe/main/docs/figure/reme_logo.png" alt="ReMe Logo" width="50%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
@ -27,44 +27,64 @@
|
||||||
> [0.2.x](https://github.com/agentscope-ai/ReMe/tree/v0.2.0.6) ·
|
> [0.2.x](https://github.com/agentscope-ai/ReMe/tree/v0.2.0.6) ·
|
||||||
> [MemoryScope](https://github.com/agentscope-ai/ReMe/tree/memoryscope_branch)
|
> [MemoryScope](https://github.com/agentscope-ai/ReMe/tree/memoryscope_branch)
|
||||||
|
|
||||||
🧠 ReMe 将对话和资料持续沉淀为可读、可编辑、可检索、相互链接的 Markdown 记忆。它可以与 QwenPaw、OpenClaw、Hermes 和 Claude
|
## ✨ 为什么选择 ReMe?
|
||||||
Code 等 Agent 协作,在持续整理知识的同时,始终把文件控制权留给用户。
|
|
||||||
|
|
||||||
## ✨ 核心创新
|
🧠 ReMe 将对话和资料持续沉淀为可读、可编辑、可检索、相互链接的 Markdown 记忆。QwenPaw、DeepSeek Harness 等 Agent
|
||||||
|
可以共享同一个 workspace,共同检索、维护和演化知识,而持久文件始终由用户掌控。
|
||||||
|
|
||||||
- **Memory as File, File as Memory**:以带 frontmatter 和 wikilink 的 Markdown 作为记忆节点,用户和 Agent 都能直接查看、编辑、移动和备份。
|
- **Memory as File, File as Memory**:ReMe 使用带 frontmatter 和 wikilink 的普通 Markdown 保存持久记忆。用户和 Agent
|
||||||
- **自进化知识库**:Auto Memory、Auto Resource 和 Auto Dream 把对话与资料逐步加工为 daily 记忆和长期知识,Auto Link
|
都可以使用熟悉的工具查看、编辑、移动、同步和备份;索引及生成的元数据均可重建。
|
||||||
再将关系与来源写回文件。
|
- **自进化知识库**:ReMe 将对话和资料逐步加工为 daily note 与长期知识,在保留来源的同时,持续提炼事实、偏好、
|
||||||
- **渐进式混合搜索**:融合 wikilink、BM25 和可选 embedding,从关键词匹配、语义召回到关系扩展,避免一次性将所有邻居全文塞入上下文。
|
流程经验及其关系。
|
||||||
- **Agent 友好集成**:可通过 SKILL.md + CLI 读写和维护同一个本地 workspace,也支持 HTTP、MCP 和 Python API 接入。
|
- **精准召回所需上下文。** ReMe 结合 BM25、可选 embedding 和 wikilink 展开,召回带行号的相关片段及其关系,无需把整个知识库塞入
|
||||||
|
Agent 上下文。
|
||||||
|
- **一个 workspace,可供不同 Agent 共同使用。** 个人助理、coding agent 和其他 Agent runtime 可以通过原生集成、SKILL.md、CLI、
|
||||||
|
HTTP、MCP 或 Python API 共享同一个本地记忆空间。
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="docs/figure/design-philosophy.svg" alt="ReMe 设计理念" width="92%">
|
<img src="docs/figure/design-philosophy.svg" alt="ReMe 设计理念" width="92%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 🔭 适用场景
|
## 📰 最新动态
|
||||||
|
|
||||||
- **Personal assistants**:为 [QwenPaw](https://github.com/agentscope-ai/QwenPaw)、
|
- [2026.10] - **[ReMe Studio Playground](https://reme.agentscope.io/studio/?lang=zh) 上线**:无需安装或启动后端,
|
||||||
[OpenClaw](https://github.com/openclaw/openclaw)、[Hermes](https://github.com/nousresearch/hermes-agent)
|
即可在浏览器中浏览示例记忆文件、编辑 Markdown、探索记忆关联图谱。欢迎大家[来体验](https://reme.agentscope.io/studio/?lang=zh)!
|
||||||
等个人助理提供用户可编辑的长期记忆层。
|
|
||||||
- **Coding agents**:在接入 [Claude Code](plugins/claude_code/reme) 等 coding agent 时,跨会话保留代码风格、项目背景、仓库决策和流程经验。
|
|
||||||
- **LLM Wiki**:把对话、笔记和资料转化为可检索、可追溯、可链接的 Markdown 知识库,由用户和 Agent 共同维护。
|
|
||||||
- **Self-evolving agents**:帮助 Agent 从经验中学习,把成功路径、失败尝试、可复用流程和阶段性反思沉淀为记忆。
|
|
||||||
|
|
||||||
## 📰 新闻
|
<p align="center">
|
||||||
|
<a href="https://reme.agentscope.io/studio/?lang=zh">
|
||||||
|
<img src="https://raw.githubusercontent.com/agentscope-ai/ReMe/main/reme_studio/figures/studio-overview.png" alt="ReMe Studio 工作区预览,点击体验 Playground" width="480" style="margin: 0 auto;">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
- [2026.08] - 发布 [ReMe 博客](https://agentscope-ai.github.io/ReMe/?doc=zh-reme-blog),系统介绍本地优先的记忆架构、自进化工作流、混合检索、
|
- [2026.09] - **[给记忆加上“标签”](https://reme.agentscope.io/zh/blog_20260920)发布**:介绍基于 Markdown 的实体标签、
|
||||||
主动发现与评测结果。
|
可重建 Tag Index 与标签过滤检索。
|
||||||
|
- [2026.09] - **[Hermes Agent 记忆 Provider](https://reme.agentscope.io/zh/integrations/hermes) 已可使用**:支持 HTTP 和 Embedded
|
||||||
|
两种模式,在模型调用前自动召回、每轮对话结束后异步执行 `auto_memory`。集成支持 Hermes Agent 0.21 及以上版本,后台任务也会继承当前 profile 上下文。
|
||||||
|
- [2026.09] - **[OpenClaw 插件](https://reme.agentscope.io/zh/integrations/openclaw) 发布**:可通过
|
||||||
|
[ClawHub](https://clawhub.ai/agentscope-ai/plugins/reme-openclaw-plugin) 或
|
||||||
|
[npm](https://www.npmjs.com/package/@agentscope-ai/reme-openclaw-plugin) 安装,为 OpenClaw 提供原生记忆召回、自动对话捕获和定时整理能力。
|
||||||
|
- [2026.09] - **[DeepSeek Harness 插件](https://reme.agentscope.io/zh/integrations/dsh) 发布**:可通过
|
||||||
|
[Awesome DSH Plugin](https://awesome-dsh-plugin.com/p/agentscope-ai/ReMe--integrations-dsh/) 或
|
||||||
|
[npm](https://www.npmjs.com/package/@agentscope-ai/reme-dsh-plugin) 安装,提供长期记忆指引、`reme_search`、自动记忆、Auto Dream 和 ReMe Status。
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>更多更新</summary>
|
||||||
|
|
||||||
|
- [2026.08] - **ReMe 博客发布**:[ReMe 博客](https://reme.agentscope.io/zh/reme-blog) 系统介绍了本地优先的记忆架构、
|
||||||
|
自进化工作流、混合检索、主动发现与评测结果。
|
||||||
|
- [2026.08] - **新增 ReMe 生态插件**:[每日论文](https://reme.agentscope.io/zh/plugins/daily-paper) 可自动发现、解析论文并生成文件化简报;
|
||||||
|
[Auto Fin](https://reme.agentscope.io/zh/plugins/auto-fin) 可研究最近 24 小时的主题相关财联社新闻,并结合本地记忆构建可追溯报告。欢迎体验。
|
||||||
|
- [2026.08] - **插件开发能力上线**:参考 [插件开发](https://reme.agentscope.io/zh/plugin_development) 与 [插件管理](https://reme.agentscope.io/zh/plugin_management),
|
||||||
|
为 ReMe 扩展 Component、Step 和 Job;欢迎开发并分享你的插件。
|
||||||
- [2026.08] - 基于 ReMe 的智能体工具使用
|
- [2026.08] - 基于 ReMe 的智能体工具使用
|
||||||
[经验驱动增强方法](https://reme.agentscope.io/?doc=toolmemory-zh)已发布,见
|
[经验驱动增强方法](https://reme.agentscope.io/zh/benchmarks/toolmemory) 已发布,见
|
||||||
[arXiv:2608.03403](https://arxiv.org/abs/2608.03403)。
|
[arXiv:2608.03403](https://arxiv.org/abs/2608.03403)。
|
||||||
- [2026.07] - 新增可选 Cookbook 工作流:[每日论文](https://reme.agentscope.io/?doc=daily-paper-zh)用于论文发现与解析,
|
|
||||||
[Auto Fin](https://reme.agentscope.io/?doc=auto-fin-zh)用于研究最近 24 小时的主题相关财联社新闻,通过本地记忆搜索回顾历史材料并构建
|
|
||||||
wikilink。
|
|
||||||
- [2026.07] -
|
- [2026.07] -
|
||||||
我们的论文 [Remember Me, Refine Me: A Dynamic Procedural Memory Framework for Experience-Driven Agent Evolution](https://aclanthology.org/2026.findings-acl.829/)
|
我们的论文 [Remember Me, Refine Me: A Dynamic Procedural Memory Framework for Experience-Driven Agent Evolution](https://aclanthology.org/2026.findings-acl.829/)
|
||||||
已被 Findings of ACL 2026 接收。
|
已被 Findings of ACL 2026 接收。
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
|
|
||||||
### 安装
|
### 安装
|
||||||
|
|
@ -82,8 +102,8 @@ pip install "reme-ai[core]"
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/agentscope-ai/ReMe.git
|
git clone https://github.com/agentscope-ai/ReMe.git
|
||||||
cd ReMe
|
cd ReMe
|
||||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
pip install -e reme_studio -e ".[core]"
|
||||||
cd website
|
cd reme_studio
|
||||||
npm ci
|
npm ci
|
||||||
npm run build:static
|
npm run build:static
|
||||||
cd ..
|
cd ..
|
||||||
|
|
@ -91,10 +111,22 @@ cd ..
|
||||||
|
|
||||||
静态构建要求 Node.js 22.13 或更高版本,并让源码安装可以直接使用 Studio。
|
静态构建要求 Node.js 22.13 或更高版本,并让源码安装可以直接使用 Studio。
|
||||||
|
|
||||||
### 环境变量
|
### Docker
|
||||||
|
|
||||||
如果需要 LLM 驱动的记忆演化或 embedding 检索,可以配置环境变量。embedding 默认关闭,因此默认配置不会启动 embedding 模型,也不需要
|
使用 Docker 和 Compose 2.24.0+,构建并启动包含 Studio 的 ReMe:
|
||||||
embedding API key。
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .reme
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
打开 <http://127.0.0.1:2333>,完整工作区保存在宿主机的 `./.reme`。Linux 用户的 UID/GID 不是 1000 时,请设置对应的
|
||||||
|
`REME_UID` 和 `REME_GID`。模型凭证、自定义路径、发布镜像和升级方式见 [Docker 部署](https://reme.agentscope.io/zh/docker)。
|
||||||
|
|
||||||
|
### 环境变量配置
|
||||||
|
|
||||||
|
如果需要 LLM 驱动的记忆演化或 embedding 检索,请在启动服务前配置环境变量。embedding 默认关闭,因此默认配置不会启动
|
||||||
|
embedding 模型,也不需要 embedding API key。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat > .env <<'EOF'
|
cat > .env <<'EOF'
|
||||||
|
|
@ -102,19 +134,19 @@ cat > .env <<'EOF'
|
||||||
# EMBEDDING_API_KEY=sk-xxx
|
# EMBEDDING_API_KEY=sk-xxx
|
||||||
# EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
# EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
|
|
||||||
# 必须:auto_memory、auto_resource 和 auto_dream 需要 LLM。
|
# 必须:auto_memory、auto_resource、auto_dream 和 proactive refresh 需要 LLM。
|
||||||
LLM_API_KEY=sk-xxx
|
LLM_API_KEY=sk-xxx
|
||||||
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
基础文件读写、BM25 检索、wikilink 遍历和 proactive topics 读取可以先不配置 LLM 凭证。
|
基础文件读写、BM25 检索、wikilink 遍历和 proactive topics 读取不需要 LLM 凭证,可以跳过此步骤并直接启动服务。
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> 如需启用基于 embedding 的语义检索,请取消 [`reme/config/default.yaml`](reme/config/default.yaml) 中
|
> 如需启用基于 embedding 的语义检索,请取消 [`reme/config/default.yaml`](reme/config/default.yaml) 中
|
||||||
> `components.as_embedding` 和 `components.embedding_store` 的注释,并将
|
> `components.as_embedding` 和 `components.embedding_store` 的注释,并将
|
||||||
> `components.file_store.default.embedding_store` 从 `""` 改为 `default`。完整说明见
|
> `components.file_store.default.embedding_store` 从 `""` 改为 `default`。完整说明见
|
||||||
> [记忆检索文档](docs/zh/memory_search.md)。
|
> [记忆检索文档](https://reme.agentscope.io/zh/memory_search)。
|
||||||
|
|
||||||
### 启动服务
|
### 启动服务
|
||||||
|
|
||||||
|
|
@ -136,12 +168,6 @@ reme help
|
||||||
curl -s http://127.0.0.1:2333/version -H 'Content-Type: application/json' -d '{}'
|
curl -s http://127.0.0.1:2333/version -H 'Content-Type: application/json' -d '{}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### ReMe Studio(可选)
|
|
||||||
|
|
||||||
上面的 `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
|
### 5 分钟记忆 Demo
|
||||||
|
|
||||||
服务运行后,可以写入一个记忆节点,让 ReMe 索引并检索它:
|
服务运行后,可以写入一个记忆节点,让 ReMe 索引并检索它:
|
||||||
|
|
@ -176,41 +202,62 @@ ReMe 会把 Agent 记忆保存为可读的 Markdown。
|
||||||
相关链接:[[digest/wiki/memory-as-file.md]]
|
相关链接:[[digest/wiki/memory-as-file.md]]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📚 使用指南
|
### ReMe Studio(可选)
|
||||||
|
|
||||||
下列 Markdown 文档覆盖主要使用流程,并以当前代码的运行时契约为准。
|
上面的 `core` 安装已包含 Studio。启动 ReMe 后,打开 <http://127.0.0.1:2333/> 即可浏览、编辑和搜索 workspace。
|
||||||
|
如需为基础安装单独添加 Studio,可使用 `pip install "reme-ai[web]"`。源码构建、配置和开发说明见
|
||||||
|
[ReMe Studio 指南](https://reme.agentscope.io/zh/workspace/studio)。
|
||||||
|
|
||||||
| 文档 | 主要内容 |
|
## 🤝 将 ReMe 接入你的 Agent
|
||||||
|------|----------|
|
|
||||||
| [快速开始](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 卡片。 |
|
|
||||||
| [Auto Dream](docs/zh/auto_dream.md) 与 [Auto Link](docs/zh/auto_link.md) | 将 daily 记忆整理为持续演化的 digest 节点和可读 wikilink 关系。 |
|
|
||||||
| [记忆检索](docs/zh/memory_search.md) | 使用 BM25、可选向量、RRF 融合、行号范围召回和渐进式链接扩展。 |
|
|
||||||
| [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 博客](https://agentscope-ai.github.io/ReMe/?doc=zh-reme-blog) | 了解完整产品故事、设计动机、使用示例和评测摘要。 |
|
|
||||||
|
|
||||||
## 🧑🍳 Cookbooks
|
ReMe 既可以作为本地记忆服务,通过 CLI、HTTP API 或 MCP server 接入,也可以通过 Python API 嵌入宿主进程。宿主集成可根据不同
|
||||||
|
runtime 的能力,将记忆指引、召回和捕获接入 Agent 生命周期。
|
||||||
|
|
||||||
Cookbook 是由 ReMe jobs 和 steps 组装而成的可选端到端工作流。默认配置不会开启它们;启动 ReMe 时选择对应的独立配置即可启用。后续新增的
|
| Agent | 推荐接入方式 | 接入后能力 |
|
||||||
cookbook 会继续在表格中按行追加。
|
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||||
|
| **DeepSeek Harness** | 使用 `dsh plugin --profile web add @agentscope-ai/reme-dsh-plugin` 安装 [`@agentscope-ai/reme-dsh-plugin`](https://reme.agentscope.io/zh/integrations/dsh)。 | 可配置记忆指引、`reme_search`、自动对话捕获、定时 Auto Dream 和 ReMe Status。 |
|
||||||
|
| **OpenClaw** | 使用 `openclaw plugins install clawhub:@agentscope-ai/reme-openclaw-plugin` 安装 [`@agentscope-ai/reme-openclaw-plugin`](https://reme.agentscope.io/zh/integrations/openclaw)。 | 原生记忆工具、用户触发运行前召回和自动对话捕获。 |
|
||||||
|
| **QwenPaw** | 通过 Python API 在进程内嵌入 ReMe。 | 复用宿主生命周期和模型配置,同时保持记忆本地、文件化。 |
|
||||||
|
| **Claude Code** | 启动共享的 streamable HTTP MCP service,并安装 [ReMe 插件](https://reme.agentscope.io/zh/integrations/claude-code)。 | 通过 MCP 进行语义、图关系和状态召回,并由 Stop Hook 异步捕获会话。 |
|
||||||
|
| **Hermes** | 安装 [ReMe provider](https://reme.agentscope.io/zh/integrations/hermes),并选择 HTTP 或 Embedded 模式。 | 模型调用前召回,每轮对话完成后异步执行 `auto_memory`。 |
|
||||||
|
| **Codex 及其他 CLI Agent** | 安装或复制 [ReMe Memory skill](skills/reme_memory/SKILL.md)。 | 通过 CLI 搜索、读取和写入记忆;自动捕获需要显式接入宿主生命周期。 |
|
||||||
|
|
||||||
| Cookbook | 能力 |
|
<p align="center"><b>集成演示</b></p>
|
||||||
|-----------------------------------------------|--------------------------------------------------------------------------------|
|
|
||||||
| [每日论文](https://reme.agentscope.io/?doc=daily-paper-zh) | 发现并排序论文,使用 Agent 解读 PDF,生成文件化论文笔记和五分钟简报。 |
|
|
||||||
| [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-zh) | 拉取主题相关财联社新闻,搜索 ReMe 历史材料并生成带 wikilink 的 Markdown 报告。 |
|
|
||||||
|
|
||||||
## 📁 记忆系统
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"></td>
|
||||||
|
<td width="45%" align="center"><b>Auto Memory</b></td>
|
||||||
|
<td width="45%" align="center"><b>Auto Dream</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><b>QwenPaw</b></td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/qwenpaw-auto-memory.gif" alt="QwenPaw Auto Memory 演示" width="100%">
|
||||||
|
</td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/qwenpaw-auto-dream.gif" alt="QwenPaw Auto Dream 演示" width="100%">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><b>Claude Code</b></td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/cc-auto-memory.gif" alt="Claude Code Auto Memory 演示" width="100%">
|
||||||
|
</td>
|
||||||
|
<td width="45%">
|
||||||
|
<img src="docs/figure/cc-auto-dream.gif" alt="Claude Code Auto Dream 演示" width="100%">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## 🧠 ReMe 如何工作
|
||||||
|
|
||||||
> Memory as File, File as Memory.
|
> Memory as File, File as Memory.
|
||||||
|
|
||||||
ReMe 将 **记忆视为文件**,让过滤后的对话来源记录和外部资料从 `session/`、`resource/` 渐进加工到 `daily/`,再沉淀为
|
ReMe 将 **记忆视为文件**,让过滤后的对话来源记录和外部资料从 `session/`、`resource/` 渐进加工到 `daily/`,再沉淀为
|
||||||
`digest/`。默认 workspace 是当前目录下的 `.reme/`;可通过 `workspace_dir=...` 选择其他由用户控制的位置。
|
`digest/`。默认 workspace 是当前目录下的 `.reme/`;可通过 `workspace_dir=...` 选择其他由用户控制的位置。
|
||||||
|
|
||||||
### 目录结构
|
### Workspace 结构
|
||||||
|
|
||||||
```text
|
```text
|
||||||
<workspace_dir>/
|
<workspace_dir>/
|
||||||
|
|
@ -246,17 +293,17 @@ ReMe 将 **记忆视为文件**,让过滤后的对话来源记录和外部资
|
||||||
<img src="docs/figure/reme-overview.svg" alt="ReMe 文件化记忆系统总览" width="92%">
|
<img src="docs/figure/reme-overview.svg" alt="ReMe 文件化记忆系统总览" width="92%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 🧭 记忆设计理念
|
### 记忆生命周期
|
||||||
|
|
||||||
ReMe 遵循 capture → index → consolidate → recall 的循环。workspace 文件是持久化的事实来源,`metadata/` 中的内容均可重建。
|
ReMe 遵循 capture → index → consolidate → recall 的循环。workspace 文件是持久化的事实来源,`metadata/` 中的内容均可重建。
|
||||||
|
|
||||||
| 能力 | 入口 | 作用 | 输出 |
|
| 能力 | 入口 | 作用 | 输出 |
|
||||||
|---------------------------------------------|-------------------------------------------|----------------------------------------------------------------------------------------------|--------------------------------------------------------------|
|
| ------------------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||||
| [`auto_memory`](docs/zh/auto_memory.md) | Agent hook 或 `reme auto_memory` | 提炼有长期价值的对话事实,同时保留过滤后的对话来源记录。 | `session/dialog/*.jsonl`、`daily/<date>/<generated-name>.md` |
|
| [`auto_memory`](https://reme.agentscope.io/zh/auto_memory) | Agent hook 或 `reme auto_memory` | 提炼有长期价值的对话事实,同时保留过滤后的对话来源记录。 | `session/dialog/*.jsonl`、`daily/<date>/<generated-name>.md` |
|
||||||
| [`auto_resource`](docs/zh/auto_resource.md) | 资源监听或 `reme auto_resource` | 将 `resource/` 下的文件转为带来源链接、按内容命名的 daily 卡片。 | `daily/<date>/<resource-card>.md` |
|
| [`auto_resource`](https://reme.agentscope.io/zh/auto_resource) | 资源监听或 `reme auto_resource` | 将 `resource/` 下的文件转为带来源链接、按内容命名的 daily 卡片。 | `daily/<date>/<resource-card>.md` |
|
||||||
| [`auto_index`](docs/zh/memory_search.md) | 后台监听或 `reme reindex` | 实时索引 `daily/` 和 `digest/` 中的 Markdown;全量重建还会扫描 `resource/` 和 JSONL。 | 可检索的 chunks、BM25、wikilink 图谱和可选向量 |
|
| [`auto_index`](https://reme.agentscope.io/zh/memory_search) | 后台监听或 `reme reindex` | watcher 摄取 `daily/` 和 `digest/` 中的 Markdown;`reindex` 只基于已摄取的 chunks 重建 BM25 和 Embedding。 | 可检索的 chunks、BM25、wikilink 图谱和可选向量 |
|
||||||
| [`auto_dream`](docs/zh/auto_dream.md) | `dream_cron` 或 `reme auto_dream` | 默认从最近两天内变化的文件中最多提取 5 个可复用 unit,再创建、印证、补充或修正 digest 节点。 | `digest/**`、`daily/<date>/interests.yaml` |
|
| [`auto_dream`](https://reme.agentscope.io/zh/auto_dream) | `dream_cron` 或 `reme auto_dream` | 默认从最近两天内变化的文件中最多提取 5 个可复用 unit,再创建、印证、补充或修正 digest 节点。 | `digest/**` |
|
||||||
| [`proactive`](docs/zh/proactive.md) | Agent 决定主动行动前调用 `reme proactive` | 读取 `auto_dream` 生成的 topics;是否以及如何提醒用户由宿主 Agent 决定。 | 来自 `daily/<date>/interests.yaml` 的结构化 topics |
|
| [`proactive_read`](https://reme.agentscope.io/zh/proactive) | Agent 决定主动行动前调用 `reme proactive_read` | 读取独立 proactive refresh 流程生成的 topics;是否以及如何提醒用户由宿主 Agent 决定。 | 来自 `daily/<date>/interests.yaml` 的结构化 topics |
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -280,82 +327,78 @@ ReMe 遵循 capture → index → consolidate → recall 的循环。workspace
|
||||||
搜索返回带行号范围的相关 chunks 和数量受限的 wikilink 邻居;可选向量结果通过 RRF 与 BM25 融合。
|
搜索返回带行号范围的相关 chunks 和数量受限的 wikilink 邻居;可选向量结果通过 RRF 与 BM25 融合。
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> `proactive` 只读取并暴露 Auto Dream 生成的兴趣主题,不会自行联网、发送通知或改写知识库;是否以及如何使用主题,由宿主 Agent
|
>
|
||||||
决定。
|
> `proactive_read` 只读取并暴露 proactive refresh 生成的兴趣主题,不会自行联网、发送通知或改写知识库;是否以及如何使用主题,由宿主 Agent
|
||||||
|
> 决定。
|
||||||
|
|
||||||
## 📊 性能表现
|
## 📊 评测结果
|
||||||
|
|
||||||
ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上下文中的记忆能力。下表为仓库中已公开的参考实验结果;模型、prompt、数据集和评判细节见各评测文档。
|
ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上下文中的记忆能力。下表为仓库中已公开的参考实验结果;模型、prompt、数据集和评判细节见各评测文档。
|
||||||
|
|
||||||
| 基准 | 设置 | 样本量 | Agentic 得分 | 主要检验内容 |
|
| 基准 | 设置 | 样本量 | Agentic 得分 | 主要检验内容 |
|
||||||
|-----------------------------------------------------------------|-------------|------------------:|-------------:|--------------------------------|
|
| --------------------------------------------------------------------------- | ----------- | ----------------: | -----------: | ------------------------------ |
|
||||||
| **[LongMemEval cleaned-s](https://reme.agentscope.io/?doc=longmemeval-zh)** | **整体** | **500 题** | **89.4%** | 跨会话检索、知识更新与时间推理 |
|
| **[LongMemEval cleaned-s](https://reme.agentscope.io/zh/benchmarks/longmemeval)** | **整体** | **500 题** | **89.4%** | 跨会话检索、知识更新与时间推理 |
|
||||||
| [BEAM](https://reme.agentscope.io/?doc=beam-zh) | 100K 上下文 | 20 cases / 400 题 | 66.1% | 十类长上下文记忆任务 |
|
| [BEAM](https://reme.agentscope.io/zh/benchmarks/beam) | 100K 上下文 | 20 cases / 400 题 | 66.1% | 十类长上下文记忆任务 |
|
||||||
| [BEAM](https://reme.agentscope.io/?doc=beam-zh) | 1M 上下文 | 35 cases / 700 题 | 65.0% | 超长对话设置 |
|
| [BEAM](https://reme.agentscope.io/zh/benchmarks/beam) | 1M 上下文 | 35 cases / 700 题 | 65.0% | 超长对话设置 |
|
||||||
|
|
||||||
在仓库的 [π-Bench 评测](https://reme.agentscope.io/?doc=pibench-zh)中,ReMe Agent 在 5 种用户角色上的平均 **PROC 得分为 0.580**
|
在仓库的 [π-Bench 评测](https://reme.agentscope.io/zh/benchmarks/pibench)中,ReMe Agent 在 5 种用户角色上的平均 **PROC 得分为 0.580**
|
||||||
,比相同测试模型配置的 NanoBot 高 2.4%。PROC 用于评估隐藏意图完成、针对性澄清、跨会话偏好和规范复用、跨任务依赖推断以及欠规格请求推进等主动性能力。
|
,比相同测试模型配置的 NanoBot 高 2.4%。PROC 用于评估隐藏意图完成、针对性澄清、跨会话偏好和规范复用、跨任务依赖推断以及欠规格请求推进等主动性能力。
|
||||||
|
|
||||||
## 🤝 Agent-friendly Integration
|
## 🧩 扩展与插件
|
||||||
|
|
||||||
ReMe 既可以作为本地记忆服务,通过 CLI、HTTP API 或 MCP server 接入,也可以通过 Python API 嵌入宿主进程。不同 Agent 可以选择适合自身
|
插件是可选的独立 Python distribution,可以贡献 Component、Step、Job backend 和配置,并通过配置显式启用。每日论文与 Auto Fin
|
||||||
runtime 的路径。
|
均已独立打包,使用说明分别见[每日论文](https://reme.agentscope.io/zh/plugins/daily-paper)和
|
||||||
|
[Auto Fin](https://reme.agentscope.io/zh/plugins/auto-fin)。
|
||||||
|
|
||||||
| Agent | 推荐接入方式 | 接入后能力 |
|
| 插件 | 能力 |
|
||||||
|-----------------------------------------------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
|
| ---------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||||
| **QwenPaw** | 通过 Python API 在进程内嵌入 ReMe。 | 复用宿主应用的生命周期和模型配置,同时保持 memory 本地、文件化。 |
|
| [每日论文](https://reme.agentscope.io/zh/plugins/daily-paper) | 发现并排序论文,使用 Agent 解读 PDF,生成文件化论文笔记和五分钟简报。 |
|
||||||
| **Claude Code** | 启动 streamable HTTP MCP service,并安装 [plugins/claude_code/reme](plugins/claude_code/reme)。 | MCP recall tools、`reme-memory` skill,以及自动记录会话的 Stop hook。 |
|
| [Auto Fin](https://reme.agentscope.io/zh/plugins/auto-fin) | 拉取主题相关财联社新闻,搜索 ReMe 历史材料并生成带 wikilink 的 Markdown 报告。 |
|
||||||
| **Hermes** | 启动 HTTP service,并安装 [plugins/hermes_agent](plugins/hermes_agent)。 | 在模型调用前自动召回相关记忆,并在每轮对话完成后异步调用 `auto_memory`。 |
|
|
||||||
| **Other CLI-capable agents (OpenClaw/Codex)** | 复制或安装 [skills/reme_memory/SKILL.md](skills/reme_memory/SKILL.md)。 | 通过 CLI 搜索、读取和写入记忆;自动记录需要宿主 Agent 显式接入会话生命周期。 |
|
|
||||||
|
|
||||||
<p align="center"><b>集成演示</b></p>
|
安装、查看、校验、启用和卸载 ReMe 插件的方法见[插件管理](https://reme.agentscope.io/zh/plugin_management)。
|
||||||
|
|
||||||
<table>
|
## 📚 文档
|
||||||
<tr>
|
|
||||||
<td align="center"></td>
|
|
||||||
<td width="45%" align="center"><b>Auto Memory</b></td>
|
|
||||||
<td width="45%" align="center"><b>Auto Dream</b></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><b>QwenPaw</b></td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/qwenpaw-auto-memory.gif" alt="QwenPaw Auto Memory 演示" width="100%">
|
|
||||||
</td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/qwenpaw-auto-dream.gif" alt="QwenPaw Auto Dream 演示" width="100%">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><b>Claude Code</b></td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/cc-auto-memory.gif" alt="Claude Code Auto Memory 演示" width="100%">
|
|
||||||
</td>
|
|
||||||
<td width="45%">
|
|
||||||
<img src="docs/figure/cc-auto-dream.gif" alt="Claude Code Auto Dream 演示" width="100%">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
## 🛠️ ReMe Operations
|
下列文档覆盖主要使用流程,并以当前代码的运行时契约为准。
|
||||||
|
|
||||||
|
| 文档 | 主要内容 |
|
||||||
|
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
||||||
|
| [快速开始](https://reme.agentscope.io/zh/quick_start) | 安装 ReMe、启动服务,并执行首次文件和记忆操作。 |
|
||||||
|
| [基础配置](https://reme.agentscope.io/zh/configuration) | 配置 workspace、模型、Service、Job、Component、插件和命令行覆盖。 |
|
||||||
|
| [服务与部署](https://reme.agentscope.io/zh/services) | 使用 HTTP、SSE、MCP 和 Studio,并理解默认安全边界。 |
|
||||||
|
| [Memory as File](https://reme.agentscope.io/zh/memory_as_file) | 理解 workspace 分层、frontmatter、wikilink、chunk 和文件事实来源模型。 |
|
||||||
|
| [Auto Memory](https://reme.agentscope.io/zh/auto_memory) | 保留过滤后的对话来源记录,并提炼可复用的 daily 记忆卡片。 |
|
||||||
|
| [Auto Resource](https://reme.agentscope.io/zh/auto_resource) | 导入支持的文本与图像资料,转换为可追溯来源的 daily 卡片。 |
|
||||||
|
| [Auto Dream](https://reme.agentscope.io/zh/auto_dream) 与 [Auto Link](https://reme.agentscope.io/zh/auto_link) | 将 daily 记忆整理为持续演化的 digest 节点和可读 wikilink 关系。 |
|
||||||
|
| [记忆检索](https://reme.agentscope.io/zh/memory_search) | 使用 BM25、可选向量、RRF 融合、行号范围召回和渐进式链接扩展。 |
|
||||||
|
| [Proactive](https://reme.agentscope.io/zh/proactive) | 安全读取兴趣主题,并将其接入宿主 Agent 的决策流程。 |
|
||||||
|
| [应用场景](https://reme.agentscope.io/zh/reme_scene) | 查看金融研究、研发记忆和个人知识库的完整使用示例。 |
|
||||||
|
| [框架说明](https://reme.agentscope.io/zh/framework) | 理解 Application、Job、Step、Component、service、配置和生命周期边界。 |
|
||||||
|
| [Agent 集成](https://reme.agentscope.io/zh/integrations) | 选择接口,并将 DSH、Claude Code、OpenClaw、Hermes、Codex 或其他 Agent 接入 ReMe。 |
|
||||||
|
| [DSH 插件](https://reme.agentscope.io/zh/integrations/dsh) 与 [Claude Code 插件](https://reme.agentscope.io/zh/integrations/claude-code) | 配置宿主原生召回、自动捕获、记忆整理与诊断。 |
|
||||||
|
| [CLI 与 Job API](https://reme.agentscope.io/zh/reference/cli) | 查询命令语法,以及由默认配置自动生成的 Job 参数参考。 |
|
||||||
|
| [运维与恢复](https://reme.agentscope.io/zh/operations) | 诊断服务、维护索引,并备份、迁移和恢复 workspace。 |
|
||||||
|
| [ReMe 博客](https://reme.agentscope.io/zh/reme-blog) | 了解完整产品故事、设计动机、使用示例和评测摘要。 |
|
||||||
|
|
||||||
|
## 🛠️ 常用命令
|
||||||
|
|
||||||
运行 `reme help` 可查看完整 job 列表。常用 workspace 与维护命令如下:
|
运行 `reme help` 可查看完整 job 列表。常用 workspace 与维护命令如下:
|
||||||
|
|
||||||
| 命令 | 作用 |
|
| 命令 | 作用 |
|
||||||
|-------------------------------------------|---------------------------------------------------------------|
|
| ----------------------------------------- | ------------------------------------------------------------- |
|
||||||
| `reme status` | 查看有状态数据组件的内存估算及进程 RSS。 |
|
| `reme status` | 查看有状态数据组件的内存估算及进程 RSS。 |
|
||||||
| [`reme search`](docs/zh/memory_search.md) | 默认使用 BM25 和 wikilink 检索,启用后增加向量检索。 |
|
| [`reme search`](https://reme.agentscope.io/zh/memory_search) | 默认使用 BM25 和 wikilink 检索,启用后增加向量检索。 |
|
||||||
| `reme read` / `reme write` / `reme edit` | 检查和维护 Markdown 记忆文件。 |
|
| `reme read` / `reme write` / `reme edit` | 检查和维护 Markdown 记忆文件。 |
|
||||||
| `reme traverse` / `reme graph_snapshot` | 浏览 wikilink 邻域或按类别组织的 digest 图。 |
|
| `reme traverse` / `reme graph_snapshot` | 浏览 wikilink 邻域或按类别组织的 digest 图。 |
|
||||||
| `reme chat` | 与可感知 workspace 的只读 Agent 进行流式对话;需要 LLM 凭证。 |
|
| `reme chat` | 与可感知 workspace 的只读 Agent 进行流式对话;需要 LLM 凭证。 |
|
||||||
| `reme reindex` | 基于已有文件重建检索和 wikilink 索引。 |
|
| `reme reindex` | 基于已摄取的 chunks 重建 BM25 和 Embedding 索引。 |
|
||||||
|
|
||||||
## 🤝 社区与支持
|
## 🤝 社区与贡献
|
||||||
|
|
||||||
- **问题反馈、需求与帮助**:请先查看 [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
|
- **代码贡献**:改动前建议阅读[贡献指南](https://reme.agentscope.io/zh/contributing)。架构与扩展方式以源码、schema 和测试为准。
|
||||||
和测试为准。
|
- **文档贡献**:请直接更新本仓库 `docs/en/`、`docs/zh/` 或对应 package 目录中的规范源文件;文档站点会从这些文件生成。
|
||||||
- **文档贡献**:用户可见文档请提交到[统一文档仓库](https://github.com/agentscope-ai/docs)的 `reme/<version>/{en,zh}/` 目录。
|
|
||||||
- **提交规范**:建议使用 Conventional Commits,例如 `feat(search): add link expansion option`、
|
- **提交规范**:建议使用 Conventional Commits,例如 `feat(search): add link expansion option`、
|
||||||
`docs(zh): update quick start`。
|
`docs(zh): update quick start`。
|
||||||
- **提交前检查**:提交 PR 前请尽量运行 `pre-commit run --all-files` 和 `pytest`;如有依赖 LLM、embedding 或外部服务的测试无法运行,请在
|
- **提交前检查**:提交 PR 前请尽量运行 `pre-commit run --all-files` 和 `pytest`;如有依赖 LLM、embedding 或外部服务的测试无法运行,请在
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,21 @@ include abstention, contradiction resolution, event ordering, information
|
||||||
extraction, instruction following, knowledge update, multi-session reasoning,
|
extraction, instruction following, knowledge update, multi-session reasoning,
|
||||||
preference following, summarization, and temporal reasoning.
|
preference following, summarization, and temporal reasoning.
|
||||||
|
|
||||||
> For the shared setup (dependencies, credentials, log conventions) see the
|
Install ReMe and the BEAM plugin in editable mode from the repository root:
|
||||||
> [top-level benchmark README](../README.md).
|
|
||||||
|
```bash
|
||||||
|
python -m pip install -e ".[as]"
|
||||||
|
reme plugins install ./plugins/beam --editable
|
||||||
|
reme plugins install ./plugins/beam-judge --editable
|
||||||
|
reme plugins validate beam
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner explicitly enables the installed `beam` plugin and combines its defaults with
|
||||||
|
ReMe's built-in `benchmark` preset. Editable installation keeps changes under
|
||||||
|
[`plugins/beam`](../../plugins/beam/README.md) visible without reinstalling the plugin.
|
||||||
|
Custom application config paths still work through `reme.config` and can use `extends: benchmark`.
|
||||||
|
This directory continues to own the runner, evaluation settings, dataset and outputs.
|
||||||
|
Model credentials use the environment variables declared by the shared benchmark configuration.
|
||||||
|
|
||||||
## 1. Get the Dataset
|
## 1. Get the Dataset
|
||||||
|
|
||||||
|
|
@ -59,7 +72,7 @@ python benchmark/beam/run.py --eval_only # reuse existing workspac
|
||||||
| `dataset.start_index` / `num_items` | Case pagination (`num_items` `0` = all). |
|
| `dataset.start_index` / `num_items` | Case pagination (`num_items` `0` = all). |
|
||||||
| `dataset.workspace_root` | Per-case workspace root (`benchmark/beam/workspaces/beam`). |
|
| `dataset.workspace_root` | Per-case workspace root (`benchmark/beam/workspaces/beam`). |
|
||||||
| `evaluation.num_workers` | `0` = auto, `1` = sequential, `>1` = parallel. |
|
| `evaluation.num_workers` | `0` = auto, `1` = sequential, `>1` = parallel. |
|
||||||
| `reme.config` | ReMe config used (`beam.yaml`). |
|
| `reme.config` | ReMe config used (`benchmark`). |
|
||||||
| `output.dir` | Results directory (`benchmark/beam/results`). |
|
| `output.dir` | Results directory (`benchmark/beam/results`). |
|
||||||
|
|
||||||
## 5. Outputs
|
## 5. Outputs
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,20 @@ ordering(事件排序)、information extraction(信息抽取)、instruct
|
||||||
knowledge update(知识更新)、multi-session reasoning(多会话推理)、preference following
|
knowledge update(知识更新)、multi-session reasoning(多会话推理)、preference following
|
||||||
(偏好遵循)、summarization(摘要)与 temporal reasoning(时间推理)。
|
(偏好遵循)、summarization(摘要)与 temporal reasoning(时间推理)。
|
||||||
|
|
||||||
> 公共设置(依赖、凭据、日志约定)见[总评测说明](../README_ZH.md)。
|
在仓库根目录以 editable 模式安装 ReMe 和 BEAM 插件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pip install -e ".[as]"
|
||||||
|
reme plugins install ./plugins/beam --editable
|
||||||
|
reme plugins install ./plugins/beam-judge --editable
|
||||||
|
reme plugins validate beam
|
||||||
|
```
|
||||||
|
|
||||||
|
runner 显式启用已安装的 `beam` 插件,并将插件默认配置与 ReMe 内置的 `benchmark` 配置组合。
|
||||||
|
editable 安装会让 [`plugins/beam`](../../plugins/beam/README_ZH.md) 下的源码修改直接生效,无需重复安装。
|
||||||
|
本目录继续保留评测参数、数据集及输出。自定义完整应用配置路径仍可通过 `reme.config` 指定,
|
||||||
|
并可使用 `extends: benchmark`。
|
||||||
|
模型凭据通过公共 benchmark 配置中声明的环境变量设置。
|
||||||
|
|
||||||
## 1. 获取数据集
|
## 1. 获取数据集
|
||||||
|
|
||||||
|
|
@ -55,7 +68,7 @@ python benchmark/beam/run.py --eval_only # 复用已有工作区
|
||||||
| `dataset.start_index` / `num_items` | case 分页(`num_items` 为 `0` 表示全部)。 |
|
| `dataset.start_index` / `num_items` | case 分页(`num_items` 为 `0` 表示全部)。 |
|
||||||
| `dataset.workspace_root` | case 工作区根目录(`benchmark/beam/workspaces/beam`)。 |
|
| `dataset.workspace_root` | case 工作区根目录(`benchmark/beam/workspaces/beam`)。 |
|
||||||
| `evaluation.num_workers` | `0` = 自动,`1` = 串行,`>1` = 并行。 |
|
| `evaluation.num_workers` | `0` = 自动,`1` = 串行,`>1` = 并行。 |
|
||||||
| `reme.config` | 使用的 ReMe 配置(`beam.yaml`)。 |
|
| `reme.config` | 使用的 ReMe 配置(`benchmark`)。 |
|
||||||
| `output.dir` | 结果目录(`benchmark/beam/results`)。 |
|
| `output.dir` | 结果目录(`benchmark/beam/results`)。 |
|
||||||
|
|
||||||
## 5. 输出
|
## 5. 输出
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ evaluation:
|
||||||
compress_session: false # true = compress session chunks in search_v2 (query-aware); false = no compression
|
compress_session: false # true = compress session chunks in search_v2 (query-aware); false = no compression
|
||||||
|
|
||||||
reme:
|
reme:
|
||||||
config: "beam.yaml" # reme config (in reme/config/)
|
config: "benchmark" # shared ReMe benchmark preset
|
||||||
|
plugins: [beam, beam-judge]
|
||||||
|
|
||||||
output:
|
output:
|
||||||
dir: "benchmark/beam/results"
|
dir: "benchmark/beam/results"
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import yaml
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# Load .env from project root
|
# Load .env from project root
|
||||||
_PROJECT_ROOT = Path(__file__).parent.parent.parent
|
_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||||
load_dotenv(_PROJECT_ROOT / ".env")
|
load_dotenv(_PROJECT_ROOT / ".env")
|
||||||
|
|
||||||
# Workspace root — read from config.yaml (dataset.workspace_root)
|
# Workspace root — read from config.yaml (dataset.workspace_root)
|
||||||
|
|
@ -151,6 +151,23 @@ def load_eval_config(config_path: str | None = None) -> dict:
|
||||||
return yaml.safe_load(raw)
|
return yaml.safe_load(raw)
|
||||||
|
|
||||||
|
|
||||||
|
def create_reme_app(config: str = "benchmark", **overrides):
|
||||||
|
"""Create an app with the BEAM candidate and judge plugins enabled.
|
||||||
|
|
||||||
|
Plugin discovery remains environment-based; editable installation keeps local
|
||||||
|
plugin source changes visible to every multiprocessing worker.
|
||||||
|
"""
|
||||||
|
from reme import Application
|
||||||
|
from reme.config import resolve_app_config
|
||||||
|
|
||||||
|
enabled_plugins = list(overrides.pop("plugins", ()) or ())
|
||||||
|
for plugin in ("beam", "beam-judge"):
|
||||||
|
if plugin not in enabled_plugins:
|
||||||
|
enabled_plugins.append(plugin)
|
||||||
|
app_config = resolve_app_config(config=config, plugins=enabled_plugins, **overrides)
|
||||||
|
return Application(**app_config)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# BEAM data loading
|
# BEAM data loading
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -319,8 +336,6 @@ async def evaluate_case(eval_config: dict, case_id: str, eval_only: bool = False
|
||||||
Returns:
|
Returns:
|
||||||
A results dict with all questions, answers, and judgments.
|
A results dict with all questions, answers, and judgments.
|
||||||
"""
|
"""
|
||||||
from reme import Application
|
|
||||||
from reme.config import resolve_app_config
|
|
||||||
|
|
||||||
dataset_cfg = eval_config["dataset"]
|
dataset_cfg = eval_config["dataset"]
|
||||||
chat_size = dataset_cfg["chat_size"]
|
chat_size = dataset_cfg["chat_size"]
|
||||||
|
|
@ -375,15 +390,15 @@ async def evaluate_case(eval_config: dict, case_id: str, eval_only: bool = False
|
||||||
force_init=True,
|
force_init=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
cfg = resolve_app_config(
|
app = create_reme_app(
|
||||||
config=eval_config["reme"]["config"],
|
config=eval_config["reme"]["config"],
|
||||||
|
plugins=eval_config["reme"].get("plugins", ()),
|
||||||
workspace_dir=workspace_dir,
|
workspace_dir=workspace_dir,
|
||||||
log_to_console=output_cfg.get("log_to_console", True),
|
log_to_console=output_cfg.get("log_to_console", True),
|
||||||
log_to_file=output_cfg.get("log_to_file", False),
|
log_to_file=output_cfg.get("log_to_file", False),
|
||||||
enable_logo=False,
|
enable_logo=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
app = Application(**cfg)
|
|
||||||
await app.start()
|
await app.start()
|
||||||
|
|
||||||
from reme.utils.evaluation_interface import check_agent_token_usage # noqa: E402
|
from reme.utils.evaluation_interface import check_agent_token_usage # noqa: E402
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,21 @@ agentic (ReAct) mode, and scores the answer with an LLM-as-judge.
|
||||||
Question types include single-session (user / assistant / preference),
|
Question types include single-session (user / assistant / preference),
|
||||||
multi-session reasoning, knowledge update, and temporal reasoning.
|
multi-session reasoning, knowledge update, and temporal reasoning.
|
||||||
|
|
||||||
> For the shared setup (dependencies, credentials, log conventions) see the
|
Install ReMe and the LongMemEval plugin in editable mode from the repository root:
|
||||||
> [top-level benchmark README](../README.md).
|
|
||||||
|
```bash
|
||||||
|
python -m pip install -e ".[as]"
|
||||||
|
reme plugins install ./plugins/lme --editable
|
||||||
|
reme plugins install ./plugins/lme-judge --editable
|
||||||
|
reme plugins validate lme
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner explicitly enables the installed `lme` plugin and combines its defaults with
|
||||||
|
ReMe's built-in `benchmark` preset. Editable installation keeps changes under
|
||||||
|
[`plugins/lme`](../../plugins/lme/README.md) visible without reinstalling the plugin.
|
||||||
|
Custom application config paths still work through `reme.config` and can use `extends: benchmark`.
|
||||||
|
This directory continues to own the runner, evaluation settings, dataset and outputs.
|
||||||
|
Model credentials use the environment variables declared by the shared benchmark configuration.
|
||||||
|
|
||||||
## 1. Get the Dataset
|
## 1. Get the Dataset
|
||||||
|
|
||||||
|
|
@ -46,7 +59,8 @@ python benchmark/longmemeval/run.py --eval_only # reuse existing w
|
||||||
|
|
||||||
1. Load the dataset (ground truth is embedded in the data file).
|
1. Load the dataset (ground truth is embedded in the data file).
|
||||||
2. For each item, create an isolated workspace and ingest sessions in chronological order.
|
2. For each item, create an isolated workspace and ingest sessions in chronological order.
|
||||||
3. Trigger `auto_dream` when consecutive sessions cross the configured hour (default 23:00).
|
3. If a custom application configuration enables `auto_dream`, trigger it when sessions cross the configured hour
|
||||||
|
(default 23:00). The packaged preset leaves it disabled.
|
||||||
4. Answer each question via agentic (ReAct) mode.
|
4. Answer each question via agentic (ReAct) mode.
|
||||||
5. Judge the answer (binary yes/no) with the `answer_judge` job and print per-type accuracy.
|
5. Judge the answer (binary yes/no) with the `answer_judge` job and print per-type accuracy.
|
||||||
|
|
||||||
|
|
@ -60,7 +74,7 @@ python benchmark/longmemeval/run.py --eval_only # reuse existing w
|
||||||
| `dataset.workspace_root` | Per-item workspace root (`benchmark/longmemeval/workspaces/longmemeval-s`). |
|
| `dataset.workspace_root` | Per-item workspace root (`benchmark/longmemeval/workspaces/longmemeval-s`). |
|
||||||
| `evaluation.num_workers` | `0` = auto (cpu-2), `1` = sequential, `>1` = parallel. |
|
| `evaluation.num_workers` | `0` = auto (cpu-2), `1` = sequential, `>1` = parallel. |
|
||||||
| `evaluation.filter_future_sessions` | Only ingest sessions with timestamp ≤ `question_date`. |
|
| `evaluation.filter_future_sessions` | Only ingest sessions with timestamp ≤ `question_date`. |
|
||||||
| `reme.config` | ReMe config used (`lme.yaml`). |
|
| `reme.config` | ReMe config used (`benchmark`). |
|
||||||
| `reme.dream_trigger_hour` / `dream_scan_days` / `dream_max_units` | Dream triggering behavior. |
|
| `reme.dream_trigger_hour` / `dream_scan_days` / `dream_max_units` | Dream triggering behavior. |
|
||||||
| `output.dir` | Results directory (`benchmark/longmemeval/results`). |
|
| `output.dir` | Results directory (`benchmark/longmemeval/results`). |
|
||||||
|
|
||||||
|
|
@ -93,4 +107,4 @@ agentscope==2.0.4.post1, conda reme env, 32 workers, eval-only (reusing prebuilt
|
||||||
| single-session-preference | 0.633 | 36,802 | 818 | 37,620 | 3.60 |
|
| single-session-preference | 0.633 | 36,802 | 818 | 37,620 | 3.60 |
|
||||||
| single-session-user | 0.986 | 27,433 | 359 | 27,792 | 2.60 |
|
| single-session-user | 0.986 | 27,433 | 359 | 27,792 | 2.60 |
|
||||||
| temporal-reasoning | 0.902 | 62,674 | 985 | 63,659 | 4.97 |
|
| temporal-reasoning | 0.902 | 62,674 | 985 | 63,659 | 4.97 |
|
||||||
| **OVERALL** | **0.894** | **43,448** | **876** | **44,324** | **3.69** |
|
| **OVERALL** | **0.894** | **43,448** | **876** | **44,324** | **3.69** |
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,20 @@ LongMemEval 是一个面向**多轮多会话历史的长期记忆能力**的评
|
||||||
|
|
||||||
题型包括单会话(user / assistant / preference)、多会话推理、知识更新与时间推理等。
|
题型包括单会话(user / assistant / preference)、多会话推理、知识更新与时间推理等。
|
||||||
|
|
||||||
> 公共设置(依赖、凭据、日志约定)见[总评测说明](../README_ZH.md)。
|
在仓库根目录以 editable 模式安装 ReMe 和 LongMemEval 插件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pip install -e ".[as]"
|
||||||
|
reme plugins install ./plugins/lme --editable
|
||||||
|
reme plugins install ./plugins/lme-judge --editable
|
||||||
|
reme plugins validate lme
|
||||||
|
```
|
||||||
|
|
||||||
|
runner 显式启用已安装的 `lme` 插件,并将插件默认配置与 ReMe 内置的 `benchmark` 配置组合。
|
||||||
|
editable 安装会让 [`plugins/lme`](../../plugins/lme/README_ZH.md) 下的源码修改直接生效,无需重复安装。
|
||||||
|
本目录继续保留评测参数、数据集及输出。自定义完整应用配置路径仍可通过 `reme.config` 指定,
|
||||||
|
并可使用 `extends: benchmark`。
|
||||||
|
模型凭据通过公共 benchmark 配置中声明的环境变量设置。
|
||||||
|
|
||||||
## 1. 获取数据集
|
## 1. 获取数据集
|
||||||
|
|
||||||
|
|
@ -41,7 +54,7 @@ python benchmark/longmemeval/run.py --eval_only # 复用已有工
|
||||||
|
|
||||||
1. 加载数据集(ground truth 已内嵌在数据文件中)。
|
1. 加载数据集(ground truth 已内嵌在数据文件中)。
|
||||||
2. 为每个条目创建独立工作区,按时间顺序摄入会话。
|
2. 为每个条目创建独立工作区,按时间顺序摄入会话。
|
||||||
3. 当相邻会话跨越配置的时刻(默认 23:00)时触发 `auto_dream`。
|
3. 若自定义应用配置启用了 `auto_dream`,在相邻会话跨越配置时刻(默认 23:00)时触发;插件预设保持关闭。
|
||||||
4. 以 agentic(ReAct)模式回答每个问题。
|
4. 以 agentic(ReAct)模式回答每个问题。
|
||||||
5. 通过 `answer_judge` 任务对答案做二元(yes/no)评判,并输出各类型准确率。
|
5. 通过 `answer_judge` 任务对答案做二元(yes/no)评判,并输出各类型准确率。
|
||||||
|
|
||||||
|
|
@ -55,7 +68,7 @@ python benchmark/longmemeval/run.py --eval_only # 复用已有工
|
||||||
| `dataset.workspace_root` | 条目工作区根目录(`benchmark/longmemeval/workspaces/longmemeval-s`)。 |
|
| `dataset.workspace_root` | 条目工作区根目录(`benchmark/longmemeval/workspaces/longmemeval-s`)。 |
|
||||||
| `evaluation.num_workers` | `0` = 自动(cpu-2),`1` = 串行,`>1` = 并行。 |
|
| `evaluation.num_workers` | `0` = 自动(cpu-2),`1` = 串行,`>1` = 并行。 |
|
||||||
| `evaluation.filter_future_sessions` | 仅摄入时间戳 ≤ `question_date` 的会话。 |
|
| `evaluation.filter_future_sessions` | 仅摄入时间戳 ≤ `question_date` 的会话。 |
|
||||||
| `reme.config` | 使用的 ReMe 配置(`lme.yaml`)。 |
|
| `reme.config` | 使用的 ReMe 配置(`benchmark`)。 |
|
||||||
| `reme.dream_trigger_hour` / `dream_scan_days` / `dream_max_units` | dream 触发行为。 |
|
| `reme.dream_trigger_hour` / `dream_scan_days` / `dream_max_units` | dream 触发行为。 |
|
||||||
| `output.dir` | 结果目录(`benchmark/longmemeval/results`)。 |
|
| `output.dir` | 结果目录(`benchmark/longmemeval/results`)。 |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ dataset:
|
||||||
workspace_root: "benchmark/longmemeval/workspaces/longmemeval-s" # workspace root for item workspaces
|
workspace_root: "benchmark/longmemeval/workspaces/longmemeval-s" # workspace root for item workspaces
|
||||||
|
|
||||||
evaluation:
|
evaluation:
|
||||||
# LLM-as-judge uses the 'judge' as_llm component defined in lme.yaml
|
# LLM-as-judge uses the 'judge' as_llm component defined in benchmark.yaml
|
||||||
# Model and credentials are configured there (reading from .env)
|
# Model and credentials are configured there (reading from .env)
|
||||||
# Judgment is always binary (yes/no) — defined in lme/llm_judge.yaml
|
# Judgment is always binary (yes/no) — defined in lme/llm_judge.yaml
|
||||||
num_workers: 32 # 0 = auto (cpu_count - 2, min 1); 1 = sequential; >1 = parallel
|
num_workers: 32 # 0 = auto (cpu_count - 2, min 1); 1 = sequential; >1 = parallel
|
||||||
|
|
@ -18,7 +18,8 @@ evaluation:
|
||||||
compress_session: false # true = compress session chunks in search_v2 (query-aware); false = no compression
|
compress_session: false # true = compress session chunks in search_v2 (query-aware); false = no compression
|
||||||
|
|
||||||
reme:
|
reme:
|
||||||
config: "lme.yaml" # reme config to use (in reme/config/)
|
config: "benchmark" # runner enables both plugins below
|
||||||
|
plugins: [lme, lme-judge]
|
||||||
# Dream trigger: when gap between consecutive sessions crosses this hour (23:00)
|
# Dream trigger: when gap between consecutive sessions crosses this hour (23:00)
|
||||||
dream_trigger_hour: 23
|
dream_trigger_hour: 23
|
||||||
# Dream scan_days for each trigger
|
# Dream scan_days for each trigger
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import yaml
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# Load .env from project root
|
# Load .env from project root
|
||||||
_PROJECT_ROOT = Path(__file__).parent.parent.parent
|
_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||||
load_dotenv(_PROJECT_ROOT / ".env")
|
load_dotenv(_PROJECT_ROOT / ".env")
|
||||||
|
|
||||||
# Workspace root for evaluation items — read from config.yaml (dataset.workspace_root)
|
# Workspace root for evaluation items — read from config.yaml (dataset.workspace_root)
|
||||||
|
|
@ -150,6 +150,23 @@ def load_eval_config(config_path: str | None = None) -> dict:
|
||||||
return yaml.safe_load(raw)
|
return yaml.safe_load(raw)
|
||||||
|
|
||||||
|
|
||||||
|
def create_reme_app(config: str = "benchmark", **overrides):
|
||||||
|
"""Create an app with the LongMemEval candidate and judge plugins enabled.
|
||||||
|
|
||||||
|
Plugin discovery remains environment-based; editable installation keeps local
|
||||||
|
plugin source changes visible to every multiprocessing worker.
|
||||||
|
"""
|
||||||
|
from reme import Application
|
||||||
|
from reme.config import resolve_app_config
|
||||||
|
|
||||||
|
enabled_plugins = list(overrides.pop("plugins", ()) or ())
|
||||||
|
for plugin in ("lme", "lme-judge"):
|
||||||
|
if plugin not in enabled_plugins:
|
||||||
|
enabled_plugins.append(plugin)
|
||||||
|
app_config = resolve_app_config(config=config, plugins=enabled_plugins, **overrides)
|
||||||
|
return Application(**app_config)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Date utilities
|
# Date utilities
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -257,8 +274,6 @@ async def evaluate_item(item: dict, eval_config: dict, item_index: int, eval_onl
|
||||||
using the existing workspace. Useful for re-evaluating different query
|
using the existing workspace. Useful for re-evaluating different query
|
||||||
configurations without re-ingesting sessions.
|
configurations without re-ingesting sessions.
|
||||||
"""
|
"""
|
||||||
from reme import Application
|
|
||||||
from reme.config import resolve_app_config
|
|
||||||
from reme.utils.evaluation_interface import track_agent_token_usage, track_job_counts
|
from reme.utils.evaluation_interface import track_agent_token_usage, track_job_counts
|
||||||
|
|
||||||
reme_cfg = eval_config["reme"]
|
reme_cfg = eval_config["reme"]
|
||||||
|
|
@ -325,15 +340,15 @@ async def evaluate_item(item: dict, eval_config: dict, item_index: int, eval_onl
|
||||||
force_init=True,
|
force_init=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
cfg = resolve_app_config(
|
app = create_reme_app(
|
||||||
config=reme_cfg["config"],
|
config=reme_cfg["config"],
|
||||||
|
plugins=reme_cfg.get("plugins", ()),
|
||||||
workspace_dir=workspace_dir,
|
workspace_dir=workspace_dir,
|
||||||
log_to_console=output_cfg.get("log_to_console", True),
|
log_to_console=output_cfg.get("log_to_console", True),
|
||||||
log_to_file=output_cfg.get("log_to_file", False),
|
log_to_file=output_cfg.get("log_to_file", False),
|
||||||
enable_logo=False,
|
enable_logo=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
app = Application(**cfg)
|
|
||||||
await app.start()
|
await app.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ removed"). They need the executed tool calls in the trace. The pipeline:
|
||||||
| user_agent / judger models | `config/models/reme.yaml` |
|
| user_agent / judger models | `config/models/reme.yaml` |
|
||||||
| Agent system prompt | `bridge_reme.py` `build_system_prompt()` |
|
| Agent system prompt | `bridge_reme.py` `build_system_prompt()` |
|
||||||
| Memory retrieval limit/threshold | `--search-limit/--search-min-score` on the bridge command in `run_persona.sh` |
|
| Memory retrieval limit/threshold | `--search-limit/--search-min-score` on the bridge command in `run_persona.sh` |
|
||||||
| ReMe internal parameters | **Do not modify ReMe source**; write a dedicated config modeled on `reme/config/beam.yaml` and override via `resolve_app_config(config=...)` (see bridge `_init_reme_app`) |
|
| ReMe internal parameters | **Do not modify ReMe source**; extend the built-in `benchmark` config and override via `resolve_app_config(config=...)` (see bridge `_init_reme_app`) |
|
||||||
| Turn timeout / tool iteration cap | `config/models/reme.yaml` `run.turn_timeout`, `model.max_tool_iterations` |
|
| Turn timeout / tool iteration cap | `config/models/reme.yaml` `run.turn_timeout`, `model.max_tool_iterations` |
|
||||||
|
|
||||||
## 11. Troubleshooting
|
## 11. Troubleshooting
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ grep -h "overall_average_score\|overall_proactiveness" \
|
||||||
| user_agent / judger 模型 | `config/models/reme.yaml` |
|
| user_agent / judger 模型 | `config/models/reme.yaml` |
|
||||||
| agent system prompt | `bridge_reme.py` `build_system_prompt()` |
|
| agent system prompt | `bridge_reme.py` `build_system_prompt()` |
|
||||||
| 记忆检索条数/阈值 | `run_persona.sh` bridge 启动命令的 `--search-limit/--search-min-score` |
|
| 记忆检索条数/阈值 | `run_persona.sh` bridge 启动命令的 `--search-limit/--search-min-score` |
|
||||||
| ReMe 内部参数 | **不要改 ReMe 源码**;仿照 `reme/config/beam.yaml` 写专有配置,经 `resolve_app_config(config=...)` 覆盖(见 bridge `_init_reme_app`) |
|
| ReMe 内部参数 | **不要改 ReMe 源码**;继承内置 `benchmark` 配置,并经 `resolve_app_config(config=...)` 覆盖(见 bridge `_init_reme_app`) |
|
||||||
| 轮超时/工具迭代上限 | `config/models/reme.yaml` `run.turn_timeout`、`model.max_tool_iterations` |
|
| 轮超时/工具迭代上限 | `config/models/reme.yaml` `run.turn_timeout`、`model.max_tool_iterations` |
|
||||||
|
|
||||||
## 11. 故障排查
|
## 11. 故障排查
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
> Code: [https://github.com/WangCan1178/ExpG](https://github.com/WangCan1178/ExpG)
|
> Code: [https://github.com/WangCan1178/ExpG](https://github.com/WangCan1178/ExpG)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="gitcha.png" alt="ExpG challenges and overview" width="85%">
|
<img src="./gitcha.png" alt="ExpG challenges and overview" width="85%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Overview
|
### Overview
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
> 代码:[https://github.com/WangCan1178/ExpG](https://github.com/WangCan1178/ExpG)
|
> 代码:[https://github.com/WangCan1178/ExpG](https://github.com/WangCan1178/ExpG)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="gitcha.png" alt="ExpG 挑战与概览" width="85%">
|
<img src="./gitcha.png" alt="ExpG 挑战与概览" width="85%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### 简介
|
### 简介
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
# Auto Fin Cookbook
|
|
||||||
|
|
||||||
[中文](README_ZH.md)
|
|
||||||
|
|
||||||
Auto Fin fetches a rolling window of CLS telegraph news (24 hours by default), selects items related to configured
|
|
||||||
topics, searches ReMe for useful historical context, and writes one Chinese Markdown report with validated wikilinks.
|
|
||||||
Current news and topic selection stay in runtime memory; only the final report becomes durable memory. The
|
|
||||||
implementation lives in [`reme/steps/cookbook/auto_fin/`](../../reme/steps/cookbook/auto_fin/) and is assembled by
|
|
||||||
[`daily_cookbook.yaml`](../../reme/config/daily_cookbook.yaml).
|
|
||||||
|
|
||||||
> Auto Fin has no reliable market-price feed. It does not calculate returns, targets, or entry points and is not
|
|
||||||
> investment advice.
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m pip install -e ".[core]"
|
|
||||||
export LLM_API_KEY="your-api-key"
|
|
||||||
export LLM_MODEL_NAME="qwen3.7-plus"
|
|
||||||
export LLM_BASE_URL="https://your-provider.example/v1"
|
|
||||||
reme start config=daily_cookbook job=auto_fin
|
|
||||||
```
|
|
||||||
|
|
||||||
`LLM_MODEL_NAME` defaults to `qwen3.7-plus`. There is no built-in `LLM_BASE_URL`, so set the OpenAI-compatible endpoint
|
|
||||||
required by the selected provider.
|
|
||||||
|
|
||||||
The default topics are `黄金,机器人,半导体`. Override them per run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start config=daily_cookbook job=auto_fin topics="黄金,AI,存储芯片"
|
|
||||||
```
|
|
||||||
|
|
||||||
An empty value also uses the defaults.
|
|
||||||
|
|
||||||
## Pipeline
|
|
||||||
|
|
||||||
```text
|
|
||||||
CLS public telegraph endpoint (rolling 24 hours)
|
|
||||||
↓
|
|
||||||
normalize and deduplicate in RuntimeContext
|
|
||||||
↓
|
|
||||||
topic Agent selects real news IDs in bounded batches
|
|
||||||
↓
|
|
||||||
research Agent uses memory_search + read on historical memory
|
|
||||||
↓
|
|
||||||
validate historical wikilinks in code
|
|
||||||
↓
|
|
||||||
daily/YYYY-MM-DD/auto_fin.md
|
|
||||||
```
|
|
||||||
|
|
||||||
`auto_fin_data_step` signs and paginates the same endpoint used by the CLS website. It starts at the decision time and
|
|
||||||
stops only after covering the exact preceding 24 hours. Requests are rate-limited and retried; malformed records and
|
|
||||||
records outside the window are discarded.
|
|
||||||
|
|
||||||
`auto_fin_topic_step` receives batches of current news and returns only related `news_id` values. Code ignores unknown
|
|
||||||
IDs and deduplicates repeated IDs, then preserves the source-news order. If nothing is relevant, the job succeeds as a
|
|
||||||
skip without writing or sending a report.
|
|
||||||
|
|
||||||
`auto_fin_merge_step` receives only selected current news. It exposes `memory_search` and `read`, instructs the Agent to
|
|
||||||
search no later than yesterday, and keeps current CLS IDs, times, and titles as plain evidence. The prompt limits
|
|
||||||
wikilinks to historical Markdown actually used by the Agent; the code-level boundary independently keeps only existing,
|
|
||||||
workspace-relative Markdown targets. Missing, absolute, escaping, backslash, and self-referential targets are degraded
|
|
||||||
to their readable aliases.
|
|
||||||
|
|
||||||
Same-day reruns use the existing report as context and replace it with the revised result. The final write is atomic and
|
|
||||||
refreshes the daily index. No JSONL, intermediate Markdown, or structured Agent output is written.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
| Parameter | Default | Purpose |
|
|
||||||
|--------------------|-----------------------:|--------------------------------------------------------------------------|
|
|
||||||
| `date` | `""` | Empty uses today in Shanghai; an explicit value must equal today |
|
|
||||||
| `now` | `""` | Optional ISO 8601 decision time for testing or replay |
|
|
||||||
| `topics` | `"黄金,机器人,半导体"` | Comma-separated topics; empty also uses these defaults |
|
|
||||||
| `window_hours` | `24` | Rolling number of hours of CLS telegraph news to fetch; must be positive |
|
|
||||||
| `request_interval` | `10` | Minimum delay in seconds after every CLS request attempt; may be zero |
|
|
||||||
| `max_retries` | `3` | Maximum attempts for each CLS page request; must be at least one |
|
|
||||||
|
|
||||||
The built-in schedules run daily at 09:30, 11:30, and 18:00 in `Asia/Shanghai`.
|
|
||||||
|
|
||||||
## Output
|
|
||||||
|
|
||||||
```text
|
|
||||||
reme_workspace/daily/YYYY-MM-DD/auto_fin.md
|
|
||||||
```
|
|
||||||
|
|
||||||
The report includes a title, description, current CLS evidence, historical analysis, contextual wikilinks, and a fixed
|
|
||||||
non-investment disclaimer. Network errors and invalid Agent output fail explicitly; no relevant current news is a
|
|
||||||
successful skip. If `DINGTALK_CONVERSATION_IDS` is empty, delivery is a no-op. If it is set, the DingTalk credentials
|
|
||||||
described in the [Daily Paper cookbook](../daily_paper/README.md#6-dingtalk) are required.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pytest tests/unit/test_auto_fin.py -v
|
|
||||||
```
|
|
||||||
|
|
||||||
Unit tests mock the CLS and Agent boundaries and do not contact external services.
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
# Auto Fin Cookbook
|
|
||||||
|
|
||||||
[English](README.md)
|
|
||||||
|
|
||||||
Auto Fin 自动拉取一个滚动时间窗口内的财联社电报(默认 24 小时),按配置 topics 筛选相关新闻,搜索 ReMe 中有回顾价值的历史材料,最后写入一份带校验
|
|
||||||
wikilink 的中文 Markdown 报告。当前新闻和筛选结果只存在于本次运行内存中,只有最终报告成为持久记忆。实现位于
|
|
||||||
[`reme/steps/cookbook/auto_fin/`](../../reme/steps/cookbook/auto_fin/),并由
|
|
||||||
[`daily_cookbook.yaml`](../../reme/config/daily_cookbook.yaml) 装配。
|
|
||||||
|
|
||||||
> Auto Fin 没有可靠行情数据,不计算收益、目标价或买卖点,也不提供投资建议。
|
|
||||||
|
|
||||||
## 快速开始
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m pip install -e ".[core]"
|
|
||||||
export LLM_API_KEY="your-api-key"
|
|
||||||
export LLM_MODEL_NAME="qwen3.7-plus"
|
|
||||||
export LLM_BASE_URL="https://your-provider.example/v1"
|
|
||||||
reme start config=daily_cookbook job=auto_fin
|
|
||||||
```
|
|
||||||
|
|
||||||
`LLM_MODEL_NAME` 默认是 `qwen3.7-plus`。代码没有内置 `LLM_BASE_URL`,请设置所选服务商提供的 OpenAI 兼容 endpoint。
|
|
||||||
|
|
||||||
默认 topics 是 `黄金,机器人,半导体`。可在运行时覆盖:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start config=daily_cookbook job=auto_fin topics="黄金,AI,存储芯片"
|
|
||||||
```
|
|
||||||
|
|
||||||
传入空值也会使用默认 topics。
|
|
||||||
|
|
||||||
## 流程
|
|
||||||
|
|
||||||
```text
|
|
||||||
财联社公开电报接口(滚动24小时)
|
|
||||||
↓
|
|
||||||
在 RuntimeContext 中规范化和去重
|
|
||||||
↓
|
|
||||||
Topic Agent 分批选择真实 news_id
|
|
||||||
↓
|
|
||||||
Research Agent 使用 memory_search + read 检索历史记忆
|
|
||||||
↓
|
|
||||||
代码校验历史 wikilink
|
|
||||||
↓
|
|
||||||
daily/YYYY-MM-DD/auto_fin.md
|
|
||||||
```
|
|
||||||
|
|
||||||
`auto_fin_data_step` 使用财联社网页同源接口的签名和分页方式,从分析时刻开始向前翻页,直到完整覆盖严格的最近 24
|
|
||||||
小时。请求带有限速和重试;损坏记录及窗口外记录会被丢弃。
|
|
||||||
|
|
||||||
`auto_fin_topic_step` 分批接收当前新闻,只返回相关的 `news_id`。代码会忽略未知 ID、去除重复 ID,并保持源新闻顺序。如果没有相关新闻,Job
|
|
||||||
会成功跳过,不写报告也不发送通知。
|
|
||||||
|
|
||||||
`auto_fin_merge_step` 只接收筛选后的当前新闻,并向 Agent 开放 `memory_search` 和 `read`。历史检索截止到昨天;当前新闻以
|
|
||||||
CLS ID、时间和标题作为普通证据。Prompt 要求 Agent 只链接实际使用过的历史 Markdown;代码边界则独立保证只保留真实存在、相对
|
|
||||||
workspace 的 Markdown 目标。不存在、绝对路径、越界、带反斜杠和自引用的目标都会降级为可读 alias。
|
|
||||||
|
|
||||||
同日重跑会参考当天已有报告并覆盖为修订结果。最终写入使用原子替换并刷新当天索引;流程不会写入 JSONL、中间 Markdown 或 Agent
|
|
||||||
结构化输出。
|
|
||||||
|
|
||||||
## 参数
|
|
||||||
|
|
||||||
| 参数 | 默认值 | 作用 |
|
|
||||||
|--------------------|-----------------------:|----------------------------------------------|
|
|
||||||
| `date` | `""` | 空值使用上海时区当天;显式日期必须等于当天 |
|
|
||||||
| `now` | `""` | 测试或回放使用的 ISO 8601 分析时间 |
|
|
||||||
| `topics` | `"黄金,机器人,半导体"` | 逗号分隔的主题;空值也使用这些默认值 |
|
|
||||||
| `window_hours` | `24` | 向前抓取财联社电报的滚动小时数,必须大于 0 |
|
|
||||||
| `request_interval` | `10` | 每次财联社请求尝试后的最小等待秒数,可设为 0 |
|
|
||||||
| `max_retries` | `3` | 每页财联社请求的最大尝试次数,至少为 1 |
|
|
||||||
|
|
||||||
内置定时任务每天按 `Asia/Shanghai` 在 09:30、11:30 和 18:00 运行。
|
|
||||||
|
|
||||||
## 产物
|
|
||||||
|
|
||||||
```text
|
|
||||||
reme_workspace/daily/YYYY-MM-DD/auto_fin.md
|
|
||||||
```
|
|
||||||
|
|
||||||
报告包含标题、说明、当前 CLS 证据、历史分析、上下文 wikilink 和固定非投资建议声明。网络错误与无效 Agent 输出
|
|
||||||
会明确失败;没有相关当前新闻则成功跳过。`DINGTALK_CONVERSATION_IDS` 为空时发送步骤无副作用;设置该变量后,
|
|
||||||
还必须提供[每日论文 Cookbook](../daily_paper/README_ZH.md#6-dingtalk)中列出的钉钉凭据。
|
|
||||||
|
|
||||||
## 验证
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pytest tests/unit/test_auto_fin.py -v
|
|
||||||
```
|
|
||||||
|
|
||||||
单元测试 mock CLS 与 Agent 边界,不访问外部服务。
|
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
# Daily Paper Cookbook
|
|
||||||
|
|
||||||
[中文](README_ZH.md)
|
|
||||||
|
|
||||||
Daily Paper selects three papers from the Hugging Face Papers weekly and monthly rankings, downloads their arXiv PDFs,
|
|
||||||
and produces detailed Chinese reading notes plus a roughly five-minute Chinese brief. The implementation lives in
|
|
||||||
[`reme/steps/cookbook/daily_paper/`](../../reme/steps/cookbook/daily_paper/) and is assembled by
|
|
||||||
[`daily_cookbook.yaml`](../../reme/config/daily_cookbook.yaml).
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
The workflow requires Python 3.11 or later, the `core` dependencies, an available AgentScope LLM, and network access to
|
|
||||||
Hugging Face Papers and arXiv.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m pip install -e ".[core]"
|
|
||||||
export LLM_API_KEY="your-api-key"
|
|
||||||
export LLM_MODEL_NAME="qwen3.7-plus"
|
|
||||||
export LLM_BASE_URL="https://your-provider.example/v1"
|
|
||||||
reme start config=daily_cookbook job=daily_paper
|
|
||||||
```
|
|
||||||
|
|
||||||
The built-in LLM component defaults to:
|
|
||||||
|
|
||||||
- model: `qwen3.7-plus`
|
|
||||||
- endpoint: no built-in `LLM_BASE_URL`; set the OpenAI-compatible endpoint required by your provider
|
|
||||||
- environment variables: `LLM_API_KEY`, `LLM_MODEL_NAME`, and `LLM_BASE_URL`
|
|
||||||
|
|
||||||
Auto Fin and Daily Paper share this single `default` LLM and the `default` AgentScope wrapper. Daily Paper Select and
|
|
||||||
Analyze call the wrapper without tools, while Daily Paper Digest and Auto Fin Merge receive the read-only
|
|
||||||
`memory_search` and `read` ReMe job tools. The interactive `dingtalk_wait` step separately overrides the wrapper per
|
|
||||||
call with AgentScope `bash` and an explicit ReMe job allowlist.
|
|
||||||
|
|
||||||
The default workspace is `reme_workspace/` beneath the process working directory. Override it with
|
|
||||||
`DAILY_PAPER_WORKSPACE_DIR`.
|
|
||||||
|
|
||||||
## Pipeline
|
|
||||||
|
|
||||||
```text
|
|
||||||
Hugging Face weekly/monthly rankings
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Collect ──► Rank ──► Select 3 ──► Analyze PDFs ──► Digest ──► DingTalk (optional)
|
|
||||||
│ │
|
|
||||||
├─ PDFs ├─ daily brief
|
|
||||||
└─ paper notes └─ day index
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1. Collect
|
|
||||||
|
|
||||||
`daily_paper_collect_step` concurrently fetches:
|
|
||||||
|
|
||||||
- the Hugging Face weekly ranking for the run date's ISO week;
|
|
||||||
- the monthly ranking for the run date's calendar month; and
|
|
||||||
- Hugging Face Daily Papers for exactly the previous calendar day.
|
|
||||||
|
|
||||||
The weekly and monthly results are merged by arXiv ID while preserving both ranks. The step then excludes papers found
|
|
||||||
in yesterday's list or in the `arxiv_id` frontmatter of `daily/<date>/*.md` within the previous `history_days`.
|
|
||||||
|
|
||||||
If a Markdown file with `kind: daily-paper-brief` already exists and `force=false`, generation is skipped; the saved
|
|
||||||
brief can still proceed to DingTalk delivery. The job fails when no eligible papers remain.
|
|
||||||
|
|
||||||
### 2. Rank
|
|
||||||
|
|
||||||
`daily_paper_rank_step` uses reciprocal-rank fusion:
|
|
||||||
|
|
||||||
```text
|
|
||||||
score = 1 / (rrf_k + monthly_rank)
|
|
||||||
+ weekly_weight / (rrf_k + weekly_rank)
|
|
||||||
```
|
|
||||||
|
|
||||||
A missing rank contributes zero. Papers are ordered by fused score, upvotes, and arXiv ID. The pool is capped at
|
|
||||||
`candidate_limit`, and Rank applies no topic preference.
|
|
||||||
|
|
||||||
### 3. Select
|
|
||||||
|
|
||||||
`daily_paper_select_step` sends candidate metadata to a tool-free AgentScope agent and requires exactly three items:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"papers": [{"arxiv_id": "2601.01234", "reasoning": "A specific, verifiable reason"}]}
|
|
||||||
```
|
|
||||||
|
|
||||||
All IDs must be unique and belong to the candidate pool, and every reason must be non-empty. A validation failure is
|
|
||||||
returned to the agent for one retry. Only a non-empty `topics` value injects a personalized subject preference into the
|
|
||||||
selection prompt; it does not change the fixed count of three papers.
|
|
||||||
|
|
||||||
### 4. Analyze
|
|
||||||
|
|
||||||
`daily_paper_analyze_step` processes the selected papers in order:
|
|
||||||
|
|
||||||
1. validates a modern `YYYY.NNNN` or `YYYY.NNNNN` arXiv ID;
|
|
||||||
2. downloads the PDF to `resource/papers/<arxiv-id>.pdf`;
|
|
||||||
3. reuses an existing target whose header is `%PDF-`;
|
|
||||||
4. extracts paginated text with `pypdf`, bounded by `max_pdf_pages` and `max_pdf_chars`;
|
|
||||||
5. sends metadata, selection reasoning, and PDF text to a tool-free agent; and
|
|
||||||
6. writes a Chinese note to `daily/<date>/<Chinese-title>.md`.
|
|
||||||
|
|
||||||
Downloads use a temporary file and atomically replace the target only after validating the PDF header. They are also
|
|
||||||
bounded by `max_pdf_bytes`. There is no OCR fallback, so scanned or textless PDFs fail. When extraction is truncated,
|
|
||||||
the note records `pdf_text_truncated: true` in its frontmatter.
|
|
||||||
|
|
||||||
### 5. Digest
|
|
||||||
|
|
||||||
`daily_paper_digest_step` uses the three in-memory analyses as the factual source for the Chinese brief. It also
|
|
||||||
searches and, when needed, reads earlier daily notes to identify related coverage; those notes may only support
|
|
||||||
contextual wikilinks, not add facts about the current papers. The agent returns `title`, `desc`, and `body`. The code
|
|
||||||
then:
|
|
||||||
|
|
||||||
- strips model-generated YAML frontmatter if present;
|
|
||||||
- normalizes the Chinese title for use as a filename;
|
|
||||||
- keeps model-generated wikilinks only when they point to existing `daily/` Markdown files dated before the run date;
|
|
||||||
- deterministically appends wikilinks to all three source notes;
|
|
||||||
- writes `daily/<date>/<Chinese-brief-title>.md`; and
|
|
||||||
- rebuilds the `daily/<date>.md` day index.
|
|
||||||
|
|
||||||
Final response metadata includes the date, week/month scopes, selected arXiv IDs, selection reasons, note/PDF/brief
|
|
||||||
paths, source counts, and exclusion counts.
|
|
||||||
|
|
||||||
### 6. DingTalk
|
|
||||||
|
|
||||||
The final `dingtalk_markdown_send_step` is optional. With no conversation IDs it is a no-op. When configured, it strips
|
|
||||||
frontmatter and sends the brief body to each group in order:
|
|
||||||
|
|
||||||
```dotenv
|
|
||||||
DINGTALK_APP_KEY=your-app-key
|
|
||||||
DINGTALK_APP_SECRET=your-app-secret
|
|
||||||
DINGTALK_ROBOT_CODE=your-robot-code
|
|
||||||
DINGTALK_CONVERSATION_IDS=cid-group-one,cid-group-two
|
|
||||||
```
|
|
||||||
|
|
||||||
A failed recipient does not prevent later attempts; the step reports a combined failure after trying every group.
|
|
||||||
|
|
||||||
## Outputs
|
|
||||||
|
|
||||||
```text
|
|
||||||
reme_workspace/
|
|
||||||
├── daily/
|
|
||||||
│ ├── YYYY-MM-DD.md
|
|
||||||
│ └── YYYY-MM-DD/
|
|
||||||
│ ├── <Chinese-paper-title>.md # three, kind: daily-paper-analysis
|
|
||||||
│ └── <Chinese-brief-title>.md # one, kind: daily-paper-brief
|
|
||||||
└── resource/
|
|
||||||
└── papers/
|
|
||||||
└── <arxiv-id>.pdf
|
|
||||||
```
|
|
||||||
|
|
||||||
Each successful generation writes three analysis notes and one brief. A forced rerun can leave unrelated or previously
|
|
||||||
selected analysis notes in the same day directory; ReMe does not delete them as cleanup. Filenames come from the agent's
|
|
||||||
Chinese titles. The implementation removes unsafe path characters and resolves title collisions. Markdown and PDF
|
|
||||||
outputs are written through same-directory temporary files and atomic replacement.
|
|
||||||
|
|
||||||
## Parameters and defaults
|
|
||||||
|
|
||||||
Public job parameters:
|
|
||||||
|
|
||||||
| Parameter | Default | Purpose |
|
|
||||||
|-----------------|--------:|-----------------------------------------------------------------------------------------|
|
|
||||||
| `date` | `""` | Run date; empty uses today in the app timezone, otherwise requires `YYYY-MM-DD` |
|
|
||||||
| `force` | `false` | Regenerate even when the day's brief exists |
|
|
||||||
| `use_hf_mirror` | `false` | Use the Hugging Face mirror from `HF_MIRROR_URL`, or `https://hf-mirror.com` when unset |
|
|
||||||
| `topics` | `""` | Topics to prioritize during selection |
|
|
||||||
| `weekly_weight` | `0.7` | Weekly contribution to RRF |
|
|
||||||
| `history_days` | `30` | Prior recommendation exclusion window |
|
|
||||||
|
|
||||||
Step-level settings on the `daily_paper` job:
|
|
||||||
|
|
||||||
| Setting | Default | Purpose |
|
|
||||||
|-------------------|--------------:|----------------------------------------------------|
|
|
||||||
| `candidate_limit` | `20` | Maximum candidates sent to Select |
|
|
||||||
| `rrf_k` | `60` | RRF constant |
|
|
||||||
| `hf_timeout` | `600` seconds | Timeout for one Hugging Face request |
|
|
||||||
| `hf_max_retries` | `3` | Maximum Hugging Face attempts |
|
|
||||||
| `pdf_timeout` | `600` seconds | arXiv PDF download timeout |
|
|
||||||
| `max_pdf_bytes` | `52428800` | PDF limit, 50 MiB |
|
|
||||||
| `max_pdf_pages` | `35` | Maximum extracted pages |
|
|
||||||
| `max_pdf_chars` | `300000` | Maximum extracted PDF characters sent to the agent |
|
|
||||||
|
|
||||||
## Mirrors
|
|
||||||
|
|
||||||
The data clients use httpx's default environment handling, so `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` take effect
|
|
||||||
when present. The two data sources reach a mirror differently: Hugging Face is gated on the `use_hf_mirror` job
|
|
||||||
parameter, while arXiv is driven by its environment variable alone.
|
|
||||||
|
|
||||||
```dotenv
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Optional override; the code defaults to https://arxiv.org when unset
|
|
||||||
ARXIV_MIRROR_URL=https://export.arxiv.org
|
|
||||||
|
|
||||||
# Path-prefixed relay URLs are also supported
|
|
||||||
# HF_MIRROR_URL=http://relay-host:18080/hf
|
|
||||||
# ARXIV_MIRROR_URL=http://relay-host:18080/arxiv
|
|
||||||
```
|
|
||||||
|
|
||||||
`HF_MIRROR_URL` must implement the `/papers/...`, `/api/daily_papers`, and `/api/papers/...` routes used by the current
|
|
||||||
client. `ARXIV_MIRROR_URL` must implement `/pdf/<arxiv-id>`. A path prefix in either base URL is preserved, and a
|
|
||||||
trailing slash is optional. There is no fallback chain: whichever base URL a client selects is the only one it tries.
|
|
||||||
|
|
||||||
> **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. 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
|
|
||||||
|
|
||||||
Generate a brief for a specific date:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start \
|
|
||||||
config=daily_cookbook \
|
|
||||||
job=daily_paper \
|
|
||||||
date=2026-08-06 \
|
|
||||||
topics="Agent memory" \
|
|
||||||
history_days=30
|
|
||||||
```
|
|
||||||
|
|
||||||
Force a rerun; valid local PDFs are still reused:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start config=daily_cookbook job=daily_paper date=2026-08-06 force=true
|
|
||||||
```
|
|
||||||
|
|
||||||
Start the HTTP service and scheduled jobs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
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, 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 \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{"date":"2026-08-06","force":false,"topics":"Agent memory"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Failures and reruns
|
|
||||||
|
|
||||||
- Hugging Face HTTP failures use exponential backoff up to `hf_max_retries` attempts; invalid response payloads fail
|
|
||||||
immediately.
|
|
||||||
- Fewer than three candidates, invalid agent selection, invalid/oversized/textless PDFs, or empty agent output stop the
|
|
||||||
job.
|
|
||||||
- Papers are analyzed sequentially; PDFs and notes completed before a failure remain on disk.
|
|
||||||
- `force=true` regenerates the selected notes and the brief while reusing valid PDFs; it does not remove other notes
|
|
||||||
already present in that day's directory.
|
|
||||||
- The multi-file workflow is not transactional and has no global per-date execution lock.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
The focused unit tests mock Hugging Face, arXiv, AgentScope, and DingTalk boundaries and do not call real services:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m pip install -e ".[dev,core]"
|
|
||||||
pytest tests/unit/test_daily_paper.py -v
|
|
||||||
```
|
|
||||||
|
|
@ -1,248 +0,0 @@
|
||||||
# 每日论文 Cookbook
|
|
||||||
|
|
||||||
[English](README.md)
|
|
||||||
|
|
||||||
每日论文工作流从 Hugging Face Papers 的周榜和月榜中筛选三篇论文,下载 arXiv PDF,生成中文论文解读和一篇约五分钟可读完的中文简报。当前实现位于
|
|
||||||
[`reme/steps/cookbook/daily_paper/`](../../reme/steps/cookbook/daily_paper/),由
|
|
||||||
[`daily_cookbook.yaml`](../../reme/config/daily_cookbook.yaml) 装配。
|
|
||||||
|
|
||||||
## 快速开始
|
|
||||||
|
|
||||||
要求 Python 3.11 或更高版本、`core` 依赖、可用的 AgentScope LLM,以及能访问 Hugging Face Papers 和 arXiv 的网络。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m pip install -e ".[core]"
|
|
||||||
export LLM_API_KEY="your-api-key"
|
|
||||||
export LLM_MODEL_NAME="qwen3.7-plus"
|
|
||||||
export LLM_BASE_URL="https://your-provider.example/v1"
|
|
||||||
reme start config=daily_cookbook job=daily_paper
|
|
||||||
```
|
|
||||||
|
|
||||||
内置 LLM 组件默认配置为:
|
|
||||||
|
|
||||||
- 模型:`qwen3.7-plus`
|
|
||||||
- endpoint:无内置 `LLM_BASE_URL`;请设置服务商要求的 OpenAI 兼容 endpoint
|
|
||||||
- 环境变量:`LLM_API_KEY`、`LLM_MODEL_NAME`、`LLM_BASE_URL`
|
|
||||||
|
|
||||||
Auto Fin 和 Daily Paper 共用这一个 `default` LLM 和 `default` AgentScope wrapper。Daily Paper 的 Select 和 Analyze
|
|
||||||
调用不带工具;Daily Paper Digest 与 Auto Fin Merge 使用只读的 ReMe Job 工具 `memory_search` 和 `read`。交互式
|
|
||||||
`dingtalk_wait` Step 则会在调用时单独覆盖 wrapper,启用 AgentScope `bash` 和明确的 ReMe Job allowlist。
|
|
||||||
|
|
||||||
默认 workspace 是启动目录下的 `reme_workspace/`,可通过 `DAILY_PAPER_WORKSPACE_DIR` 覆盖。
|
|
||||||
|
|
||||||
## 工作流
|
|
||||||
|
|
||||||
```text
|
|
||||||
Hugging Face 周榜/月榜
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Collect ──► Rank ──► Select 3 篇 ──► Analyze PDF ──► Digest ──► DingTalk(可选)
|
|
||||||
│ │
|
|
||||||
├─ PDF ├─ 每日简报
|
|
||||||
└─ 论文解读 └─ 当日索引
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1. Collect
|
|
||||||
|
|
||||||
`daily_paper_collect_step` 根据运行日期并发读取:
|
|
||||||
|
|
||||||
- 该日期所在 ISO week 的 Hugging Face 周榜;
|
|
||||||
- 该日期所在自然月的 Hugging Face 月榜;
|
|
||||||
- 严格前一个自然日的 Hugging Face Daily Papers。
|
|
||||||
|
|
||||||
周榜和月榜按 arXiv ID 合并,并保留各自排名。随后排除:
|
|
||||||
|
|
||||||
- 昨日 Daily Papers 中的论文;
|
|
||||||
- `history_days` 窗口内,已出现在 `daily/<date>/*.md` frontmatter `arxiv_id` 中的论文。
|
|
||||||
|
|
||||||
如果当天已经存在 `kind: daily-paper-brief` 的 Markdown 且 `force=false`,整个生成流程会跳过;已有简报仍可进入钉钉发送步骤。没有剩余候选论文时,Job
|
|
||||||
直接失败。
|
|
||||||
|
|
||||||
### 2. Rank
|
|
||||||
|
|
||||||
`daily_paper_rank_step` 使用 reciprocal-rank fusion:
|
|
||||||
|
|
||||||
```text
|
|
||||||
score = 1 / (rrf_k + monthly_rank)
|
|
||||||
+ weekly_weight / (rrf_k + weekly_rank)
|
|
||||||
```
|
|
||||||
|
|
||||||
缺失的榜单排名贡献为零。论文按融合分、upvotes、arXiv ID 排序,候选池最多保留 `candidate_limit` 篇。Rank 阶段不应用任何主题倾向。
|
|
||||||
|
|
||||||
### 3. Select
|
|
||||||
|
|
||||||
`daily_paper_select_step` 将候选元数据交给无工具的 AgentScope Agent,并要求返回恰好三项:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"papers": [{"arxiv_id": "2601.01234", "reasoning": "具体且可核验的选择理由"}]}
|
|
||||||
```
|
|
||||||
|
|
||||||
三个 ID 必须唯一且都属于候选池,理由不能为空。校验失败后,错误信息会反馈给 Agent 并重试一次。只有非空 `topics`
|
|
||||||
会向精选提示注入个性化主题,且不会改变固定的三篇数量。
|
|
||||||
|
|
||||||
### 4. Analyze
|
|
||||||
|
|
||||||
`daily_paper_analyze_step` 按精选顺序逐篇处理:
|
|
||||||
|
|
||||||
1. 校验新版 arXiv ID 格式 `YYYY.NNNN` 或 `YYYY.NNNNN`;
|
|
||||||
2. 下载 PDF 到 `resource/papers/<arxiv-id>.pdf`;
|
|
||||||
3. 如果目标文件已存在且以 `%PDF-` 开头,直接复用;
|
|
||||||
4. 用 `pypdf` 提取分页文本,受 `max_pdf_pages` 和 `max_pdf_chars` 限制;
|
|
||||||
5. 将论文元数据、选择理由和 PDF 文本交给无工具 Agent;
|
|
||||||
6. 将中文解读写入 `daily/<date>/<中文标题>.md`。
|
|
||||||
|
|
||||||
下载采用临时文件并在校验 PDF 文件头后原子替换,同时限制 `max_pdf_bytes`。当前没有 OCR;扫描版或无文本层 PDF 会失败。提取被截断时,笔记
|
|
||||||
frontmatter 中的 `pdf_text_truncated` 会记录为 `true`。
|
|
||||||
|
|
||||||
### 5. Digest
|
|
||||||
|
|
||||||
`daily_paper_digest_step` 以内存中的三篇解读作为本期事实来源生成中文简报,同时搜索并按需读取较早的 daily
|
|
||||||
文章来识别相关报道;历史文章只能用于建立上下文 wikilink,不能用于补充本期论文事实。输出必须包含 `title`、`desc` 和 `body`
|
|
||||||
。代码会:
|
|
||||||
|
|
||||||
- 去掉模型可能生成的 YAML frontmatter;
|
|
||||||
- 规范化中文标题并用作文件名;
|
|
||||||
- 只保留指向真实存在、日期早于运行日期的 `daily/` Markdown 文件的模型生成 wikilink;
|
|
||||||
- 确定性追加三篇源笔记的 wikilink;
|
|
||||||
- 写入 `daily/<date>/<中文简报标题>.md`;
|
|
||||||
- 重建 `daily/<date>.md` 当日索引。
|
|
||||||
|
|
||||||
最终响应 metadata 包含日期、周/月范围、入选 arXiv ID、选择理由、笔记/PDF/简报路径、源榜单数量和排重数量。
|
|
||||||
|
|
||||||
### 6. DingTalk
|
|
||||||
|
|
||||||
最后的 `dingtalk_markdown_send_step` 是可选步骤。未设置群会话 ID 时无副作用跳过;配置后会去掉 frontmatter,并把简报正文依次发送给所有群:
|
|
||||||
|
|
||||||
```dotenv
|
|
||||||
DINGTALK_APP_KEY=your-app-key
|
|
||||||
DINGTALK_APP_SECRET=your-app-secret
|
|
||||||
DINGTALK_ROBOT_CODE=your-robot-code
|
|
||||||
DINGTALK_CONVERSATION_IDS=cid-group-one,cid-group-two
|
|
||||||
```
|
|
||||||
|
|
||||||
任一群发送失败不会阻止继续尝试后续群,全部尝试结束后统一报告失败。
|
|
||||||
|
|
||||||
## 产物
|
|
||||||
|
|
||||||
```text
|
|
||||||
reme_workspace/
|
|
||||||
├── daily/
|
|
||||||
│ ├── YYYY-MM-DD.md
|
|
||||||
│ └── YYYY-MM-DD/
|
|
||||||
│ ├── <中文论文标题>.md # 三篇,kind: daily-paper-analysis
|
|
||||||
│ └── <中文简报标题>.md # 一篇,kind: daily-paper-brief
|
|
||||||
└── resource/
|
|
||||||
└── papers/
|
|
||||||
└── <arxiv-id>.pdf
|
|
||||||
```
|
|
||||||
|
|
||||||
每次成功生成会写入三篇论文解读和一篇简报。强制重跑后,当日目录中可能保留其他内容或此前入选论文的解读;ReMe
|
|
||||||
不会把它们作为清理对象删除。文件名来自 Agent 返回的中文标题。代码会清理路径不安全字符,并处理同名文件。Markdown 和 PDF
|
|
||||||
都通过同目录临时文件写入后原子替换。
|
|
||||||
|
|
||||||
## 参数与默认值
|
|
||||||
|
|
||||||
可在调用时传入的 Job 参数:
|
|
||||||
|
|
||||||
| 参数 | 默认值 | 作用 |
|
|
||||||
|-----------------|--------:|----------------------------------------------------------------------------------------------|
|
|
||||||
| `date` | `""` | 运行日期;空值使用应用时区当天,非空值必须为 `YYYY-MM-DD` |
|
|
||||||
| `force` | `false` | 已有当日简报时仍重新生成 |
|
|
||||||
| `use_hf_mirror` | `false` | 是否使用 Hugging Face 镜像站;优先读取 `HF_MIRROR_URL`,未配置时使用 `https://hf-mirror.com` |
|
|
||||||
| `topics` | `""` | 精选论文时优先考虑的主题 |
|
|
||||||
| `weekly_weight` | `0.7` | RRF 中周榜权重 |
|
|
||||||
| `history_days` | `30` | 历史推荐排重窗口 |
|
|
||||||
|
|
||||||
`daily_paper` Job 的步骤级配置:
|
|
||||||
|
|
||||||
| 配置 | 默认值 | 作用 |
|
|
||||||
|-------------------|-----------:|------------------------------|
|
|
||||||
| `candidate_limit` | `20` | 送入 Select 的最大候选数 |
|
|
||||||
| `rrf_k` | `60` | RRF 常数 |
|
|
||||||
| `hf_timeout` | `600` 秒 | Hugging Face 单次请求超时 |
|
|
||||||
| `hf_max_retries` | `3` | Hugging Face 最大尝试次数 |
|
|
||||||
| `pdf_timeout` | `600` 秒 | arXiv PDF 下载超时 |
|
|
||||||
| `max_pdf_bytes` | `52428800` | PDF 上限,50 MiB |
|
|
||||||
| `max_pdf_pages` | `35` | 最多提取页数 |
|
|
||||||
| `max_pdf_chars` | `300000` | 最多送入 Agent 的 PDF 字符数 |
|
|
||||||
|
|
||||||
## 镜像站
|
|
||||||
|
|
||||||
数据客户端使用 httpx 默认的环境处理,因此存在 `HTTP_PROXY`、`HTTPS_PROXY` 或 `NO_PROXY` 时会自动生效。两个数据源启用镜像的方式不同:Hugging
|
|
||||||
Face 由 `use_hf_mirror` 任务参数控制,arXiv 仅由环境变量驱动。
|
|
||||||
|
|
||||||
```dotenv
|
|
||||||
# 内置 daily_paper_cron 定时任务默认启用镜像站;设为 false 可改用官方服务
|
|
||||||
DAILY_PAPER_USE_HF_MIRROR=false
|
|
||||||
|
|
||||||
# 仅在手动任务或定时任务启用镜像时读取;未配置时使用 https://hf-mirror.com
|
|
||||||
HF_MIRROR_URL=https://hf-mirror.com
|
|
||||||
|
|
||||||
# 可选覆盖;未设置时代码使用 https://arxiv.org
|
|
||||||
ARXIV_MIRROR_URL=https://export.arxiv.org
|
|
||||||
|
|
||||||
# 也支持带路径前缀的中转地址
|
|
||||||
# HF_MIRROR_URL=http://relay-host:18080/hf
|
|
||||||
# ARXIV_MIRROR_URL=http://relay-host:18080/arxiv
|
|
||||||
```
|
|
||||||
|
|
||||||
`HF_MIRROR_URL` 必须提供当前代码使用的 `/papers/...`、`/api/daily_papers` 和 `/api/papers/...` 路径。`ARXIV_MIRROR_URL`
|
|
||||||
必须支持 `/pdf/<arxiv-id>`。两种 base URL 都会保留路径前缀,末尾 `/` 可有可无。不存在备用地址回退:客户端选定哪个 base
|
|
||||||
URL,就只访问该地址。
|
|
||||||
|
|
||||||
> **行为变更:** 以往只要设置 `HF_MIRROR_URL` 就会改变 Hugging Face
|
|
||||||
> 的访问地址;现在该变量仅在任务启用镜像时才会读取,否则直接访问官方站点,并输出一条“已忽略该变量”的告警日志。手动调用需传入
|
|
||||||
> `use_hf_mirror=true`。内置 `daily_paper_cron` 定时任务默认启用镜像;设置 `DAILY_PAPER_USE_HF_MIRROR=false`
|
|
||||||
> 可让该定时任务改用官方服务。
|
|
||||||
|
|
||||||
## 运行方式
|
|
||||||
|
|
||||||
生成指定日期的简报:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start \
|
|
||||||
config=daily_cookbook \
|
|
||||||
job=daily_paper \
|
|
||||||
date=2026-08-06 \
|
|
||||||
topics="Agent memory" \
|
|
||||||
history_days=30
|
|
||||||
```
|
|
||||||
|
|
||||||
强制重跑;有效的本地 PDF 仍会复用:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start config=daily_cookbook job=daily_paper date=2026-08-06 force=true
|
|
||||||
```
|
|
||||||
|
|
||||||
启动 HTTP 服务和定时任务:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
reme start config=daily_cookbook
|
|
||||||
```
|
|
||||||
|
|
||||||
内置服务监听 `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 \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{"date":"2026-08-06","force":false,"topics":"Agent memory"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 失败与重跑
|
|
||||||
|
|
||||||
- Hugging Face HTTP 请求失败会指数退避重试,最多尝试 `hf_max_retries` 次;响应数据格式无效时立即失败。
|
|
||||||
- 候选少于三篇、Agent 精选不合法、PDF 无效/过大/无文本或 Agent 输出为空都会终止 Job。
|
|
||||||
- 三篇论文按顺序处理;中途失败时,之前已完成的 PDF 和笔记会保留。
|
|
||||||
- `force=true` 会重新生成本次入选论文的解读和简报,并复用有效 PDF;不会删除当日目录中已有的其他笔记。
|
|
||||||
- 多文件流程不是事务,也没有同一日期的全局运行锁。
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
单元测试会 mock Hugging Face、arXiv、AgentScope 和 DingTalk 边界,不访问真实服务:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m pip install -e ".[dev,core]"
|
|
||||||
pytest tests/unit/test_daily_paper.py -v
|
|
||||||
```
|
|
||||||
18
deploy/docker/example.env
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Copy to the repository's .env for Docker Compose; keep real credentials private.
|
||||||
|
# File operations and BM25 search work without model credentials.
|
||||||
|
LLM_API_KEY=
|
||||||
|
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
|
LLM_MODEL_NAME=qwen3.7-plus
|
||||||
|
|
||||||
|
# Host settings: create this directory before starting Compose.
|
||||||
|
REME_DATA_DIR=./.reme
|
||||||
|
REME_PUBLISHED_PORT=2333
|
||||||
|
|
||||||
|
# On Linux, set these to the outputs of `id -u` and `id -g` so files remain yours.
|
||||||
|
REME_UID=1000
|
||||||
|
REME_GID=1000
|
||||||
|
|
||||||
|
# Optional container settings.
|
||||||
|
# REME_TIMEZONE=Asia/Shanghai
|
||||||
|
# REME_CONFIG=/etc/reme/config.yaml
|
||||||
|
# REME_IMAGE=ghcr.io/agentscope-ai/reme:main
|
||||||
100
deploy/docker/reme_container.py
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
"""Container startup and health checks using ReMe's existing CLI contract."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
from urllib.request import ProxyHandler, Request, build_opener
|
||||||
|
|
||||||
|
HEALTH_STATE_PATH = Path("/tmp/reme-health.json")
|
||||||
|
_ENV_OVERRIDES = {
|
||||||
|
"REME_CONFIG": "config",
|
||||||
|
"REME_WORKSPACE_DIR": "workspace_dir",
|
||||||
|
"REME_HOST": "service.host",
|
||||||
|
"REME_PORT": "service.port",
|
||||||
|
"REME_TIMEZONE": "timezone",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def start_command(arguments: list[str], environment: dict[str, str]) -> list[str]:
|
||||||
|
"""Add container defaults only to `start`; explicit CLI arguments win."""
|
||||||
|
from reme.config import deep_merge_config, parse_kwargs
|
||||||
|
|
||||||
|
defaults = parse_kwargs(
|
||||||
|
"log_to_file=false",
|
||||||
|
*[f"{key}={json.dumps(environment[name])}" for name, key in _ENV_OVERRIDES.items() if environment.get(name)],
|
||||||
|
)
|
||||||
|
# Docker environment values are strings; the HTTP service expects an integer port.
|
||||||
|
if environment.get("REME_PORT"):
|
||||||
|
defaults["service"]["port"] = int(environment["REME_PORT"])
|
||||||
|
overrides = deep_merge_config(defaults, parse_kwargs(*arguments))
|
||||||
|
return ["reme", "start", *[f"{key}={json.dumps(value, ensure_ascii=False)}" for key, value in overrides.items()]]
|
||||||
|
|
||||||
|
|
||||||
|
def write_health_state(command: list[str], state_path: Path = HEALTH_STATE_PATH) -> None:
|
||||||
|
"""Record only the effective HTTP address, never credentials or user data."""
|
||||||
|
from reme.components.service.cli_service import prepare_start_config
|
||||||
|
from reme.config import parse_kwargs
|
||||||
|
from reme.constants import REME_DEFAULT_HOST, REME_DEFAULT_PORT, normalize_connect_host
|
||||||
|
from reme.plugin import resolve_plugin_runtime
|
||||||
|
|
||||||
|
config = resolve_plugin_runtime(prepare_start_config(parse_kwargs(*command[2:]))).config
|
||||||
|
service = config.get("service") or {}
|
||||||
|
if service.get("backend") != "http":
|
||||||
|
return
|
||||||
|
host = normalize_connect_host(service.get("host") or REME_DEFAULT_HOST)
|
||||||
|
if host == "::":
|
||||||
|
host = "::1"
|
||||||
|
port = int(service.get("port", REME_DEFAULT_PORT))
|
||||||
|
if not 1 <= port <= 65535:
|
||||||
|
raise ValueError("service.port must be between 1 and 65535")
|
||||||
|
host = f"[{host}]" if ":" in host else host
|
||||||
|
with state_path.open("w", encoding="utf-8") as state_file:
|
||||||
|
os.chmod(state_path, 0o600)
|
||||||
|
json.dump({"url": f"http://{host}:{port}/health_check"}, state_file)
|
||||||
|
|
||||||
|
|
||||||
|
def healthcheck(state_path: Path = HEALTH_STATE_PATH) -> int:
|
||||||
|
"""Require both a successful Job and a healthy component snapshot."""
|
||||||
|
try:
|
||||||
|
state = json.loads(state_path.read_text(encoding="utf-8"))
|
||||||
|
request = Request(state["url"], data=b"{}", headers={"Content-Type": "application/json"}, method="POST")
|
||||||
|
# A deployment's outbound proxy must not intercept its local probe.
|
||||||
|
with build_opener(ProxyHandler({})).open(request, timeout=4) as response:
|
||||||
|
payload = json.load(response)
|
||||||
|
healthy = payload.get("metadata", {}).get("health", {}).get("healthy")
|
||||||
|
if payload.get("success") is True and healthy is True:
|
||||||
|
return 0
|
||||||
|
except (OSError, ValueError, KeyError, TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
print("ReMe HTTP health check failed; inspect the container logs and POST /health_check", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""Prepare startup, then replace this process so signals reach ReMe."""
|
||||||
|
arguments = sys.argv[1:]
|
||||||
|
if arguments == ["--healthcheck"]:
|
||||||
|
return healthcheck()
|
||||||
|
|
||||||
|
HEALTH_STATE_PATH.unlink(missing_ok=True)
|
||||||
|
Path(os.environ.get("HOME", "/tmp/reme-home")).mkdir(parents=True, exist_ok=True)
|
||||||
|
if not arguments:
|
||||||
|
arguments = ["start"]
|
||||||
|
start_actions = {"start", "-start", "--start"}
|
||||||
|
if len(arguments) >= 2 and arguments[0] == "reme" and arguments[1] in start_actions:
|
||||||
|
arguments = arguments[1:]
|
||||||
|
if arguments[0] in start_actions:
|
||||||
|
from reme.utils import load_env
|
||||||
|
|
||||||
|
load_env()
|
||||||
|
arguments = start_command(arguments[1:], dict(os.environ))
|
||||||
|
write_health_state(arguments)
|
||||||
|
os.execvp(arguments[0], arguments)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
24
docker-compose.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
services:
|
||||||
|
reme:
|
||||||
|
image: ${REME_IMAGE:-reme:local}
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
init: false # The image already runs tini.
|
||||||
|
user: "${REME_UID:-1000}:${REME_GID:-1000}"
|
||||||
|
ports:
|
||||||
|
- "${REME_BIND_ADDRESS:-127.0.0.1}:${REME_PUBLISHED_PORT:-2333}:${REME_PORT:-2333}"
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ${REME_DATA_DIR:-./.reme}
|
||||||
|
target: /data
|
||||||
|
bind:
|
||||||
|
create_host_path: false
|
||||||
|
env_file:
|
||||||
|
- path: ${REME_ENV_FILE:-.env}
|
||||||
|
required: false
|
||||||
|
environment:
|
||||||
|
REME_HOST: 0.0.0.0
|
||||||
|
REME_PORT: ${REME_PORT:-2333}
|
||||||
|
REME_WORKSPACE_DIR: /data
|
||||||
|
restart: unless-stopped
|
||||||
|
stop_grace_period: 60s
|
||||||
396
docs/.vitepress/config.mts
Normal file
|
|
@ -0,0 +1,396 @@
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { execFileSync } from "node:child_process";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { defineConfig, type DefaultTheme } from "vitepress";
|
||||||
|
import { legacyRoutes } from "./legacy-routes.mjs";
|
||||||
|
|
||||||
|
const sourceRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
|
const repositoryRoot = path.resolve(sourceRoot, "../../..");
|
||||||
|
const repository = "https://github.com/agentscope-ai/ReMe";
|
||||||
|
const base = process.env.DOCS_BASE || "/";
|
||||||
|
|
||||||
|
function readSourceMap(): Record<string, string> {
|
||||||
|
try {
|
||||||
|
return JSON.parse(fs.readFileSync(path.join(sourceRoot, ".source-map.json"), "utf8"));
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceMap = readSourceMap();
|
||||||
|
|
||||||
|
function collectMarkdown(directory: string, root = directory): string[] {
|
||||||
|
const files: string[] = [];
|
||||||
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||||
|
if (entry.name.startsWith(".") || entry.name === "public" || entry.name === "figure") continue;
|
||||||
|
const absolute = path.join(directory, entry.name);
|
||||||
|
if (entry.isDirectory()) files.push(...collectMarkdown(absolute, root));
|
||||||
|
else if (entry.name.endsWith(".md")) files.push(path.relative(root, absolute).replaceAll(path.sep, "/"));
|
||||||
|
}
|
||||||
|
return files.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLlmsFiles(outDir: string) {
|
||||||
|
const pages = collectMarkdown(sourceRoot);
|
||||||
|
const index = [
|
||||||
|
"# ReMe Documentation",
|
||||||
|
"",
|
||||||
|
"> Local-first, file-native memory for agents.",
|
||||||
|
"",
|
||||||
|
...pages.map((relativePath) => {
|
||||||
|
const source = fs.readFileSync(path.join(sourceRoot, relativePath), "utf8");
|
||||||
|
const title = source.match(/^#\s+(.+)$/m)?.[1]
|
||||||
|
|| source.match(/^title:\s*(.+)$/m)?.[1]
|
||||||
|
|| path.basename(relativePath, ".md");
|
||||||
|
const route = relativePath.replace(/(?:^|\/)index\.md$/, "").replace(/\.md$/, "");
|
||||||
|
return `- [${title}](https://reme.agentscope.io/${route})`;
|
||||||
|
}),
|
||||||
|
"",
|
||||||
|
];
|
||||||
|
fs.writeFileSync(path.join(outDir, "llms.txt"), index.join("\n"), "utf8");
|
||||||
|
|
||||||
|
const full = ["# ReMe Documentation", ""];
|
||||||
|
for (const relativePath of pages) {
|
||||||
|
const source = fs.readFileSync(path.join(sourceRoot, relativePath), "utf8");
|
||||||
|
full.push(`<!-- source: ${sourcePathFor(relativePath)} -->`, "", source, "", "---", "");
|
||||||
|
const pageDir = path.join(outDir, relativePath.replace(/\.md$/, ""));
|
||||||
|
fs.mkdirSync(pageDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(pageDir, "llms.txt"), source, "utf8");
|
||||||
|
}
|
||||||
|
fs.writeFileSync(path.join(outDir, "llms-full.txt"), full.join("\n"), "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourcePathFor(relativePath: string) {
|
||||||
|
return sourceMap[relativePath] || `docs/${relativePath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceLastUpdated(relativePath: string): number | undefined {
|
||||||
|
const sourcePath = sourcePathFor(relativePath);
|
||||||
|
try {
|
||||||
|
const timestamp = execFileSync("git", ["log", "-1", "--format=%ct", "--", sourcePath], {
|
||||||
|
cwd: repositoryRoot,
|
||||||
|
encoding: "utf8",
|
||||||
|
}).trim();
|
||||||
|
if (timestamp) return Number(timestamp) * 1000;
|
||||||
|
} catch {
|
||||||
|
// Fall back to the canonical file timestamp outside a Git checkout.
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return fs.statSync(path.join(repositoryRoot, sourcePath)).mtimeMs;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyRedirectScript = `(() => {
|
||||||
|
const routes = ${JSON.stringify(legacyRoutes)};
|
||||||
|
const id = new URLSearchParams(window.location.search).get("doc");
|
||||||
|
const target = id && routes[id];
|
||||||
|
const base = ${JSON.stringify(base)};
|
||||||
|
if (target) {
|
||||||
|
const destination = /^https?:/.test(target)
|
||||||
|
? target
|
||||||
|
: base.replace(/\\/$/, "") + target;
|
||||||
|
window.location.replace(destination + window.location.hash);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const root = base.endsWith("/") ? base : base + "/";
|
||||||
|
if (window.location.pathname === root) {
|
||||||
|
window.location.replace(root + "zh/" + window.location.hash);
|
||||||
|
}
|
||||||
|
})();`;
|
||||||
|
|
||||||
|
function nav(language: "zh" | "en"): DefaultTheme.NavItem[] {
|
||||||
|
const zh = language === "zh";
|
||||||
|
return [
|
||||||
|
{ text: zh ? "首页" : "Home", link: `/${language}/` },
|
||||||
|
{ text: zh ? "文档" : "Docs", link: `/${language}/quick_start` },
|
||||||
|
{ text: zh ? "体验Studio" : "Try Studio", link: `/studio/?lang=${language}`, target: "_self" },
|
||||||
|
{ text: zh ? "集成" : "Integrations", link: `/${language}/integrations` },
|
||||||
|
{ text: zh ? "插件" : "Plugins", link: `/${language}/plugin_management` },
|
||||||
|
{ text: zh ? "评测" : "Benchmarks", link: `/${language}/benchmarks/longmemeval` },
|
||||||
|
{ text: zh ? "博客" : "Blog", link: `/${language}/reme-blog` },
|
||||||
|
{ text: zh ? "常见问题" : "FAQ", link: `/${language}/faq` },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function docsSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] {
|
||||||
|
const zh = language === "zh";
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: zh ? "开始使用" : "Get Started",
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: zh ? "项目介绍" : "Introduction", link: `/${language}/overview` },
|
||||||
|
{ text: zh ? "快速开始" : "Quick Start", link: `/${language}/quick_start` },
|
||||||
|
{ text: zh ? "基础配置" : "Configuration", link: `/${language}/configuration` },
|
||||||
|
{ text: zh ? "服务与部署" : "Services and Deployment", link: `/${language}/services` },
|
||||||
|
{ text: zh ? "Docker 部署" : "Docker Deployment", link: `/${language}/docker` },
|
||||||
|
{ text: "ReMe Studio", link: `/${language}/workspace/studio` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: zh ? "核心概念" : "Core Concepts",
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: zh ? "文件即记忆" : "Memory as File", link: `/${language}/memory_as_file` },
|
||||||
|
{ text: zh ? "记忆检索" : "Memory Search", link: `/${language}/memory_search` },
|
||||||
|
{ text: zh ? "自动关联" : "Auto Link", link: `/${language}/auto_link` },
|
||||||
|
{ text: zh ? "应用场景" : "Application Scenarios", link: `/${language}/reme_scene` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: zh ? "记忆工作流" : "Memory Workflows",
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: "Auto Memory", link: `/${language}/auto_memory` },
|
||||||
|
{ text: "Auto Resource", link: `/${language}/auto_resource` },
|
||||||
|
{ text: "Auto Dream", link: `/${language}/auto_dream` },
|
||||||
|
{ text: "Proactive", link: `/${language}/proactive` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: zh ? "API 与运维" : "API and Operations",
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: "CLI", link: `/${language}/reference/cli` },
|
||||||
|
{ text: zh ? "Job API" : "Job API", link: `/${language}/reference/jobs` },
|
||||||
|
{ text: "HTTP / MCP", link: `/${language}/services#http-api` },
|
||||||
|
{ text: zh ? "诊断、备份与恢复" : "Diagnostics, Backup, and Recovery", link: `/${language}/operations` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: zh ? "开发者" : "Development",
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: zh ? "代码框架" : "Framework", link: `/${language}/framework` },
|
||||||
|
{ text: zh ? "开源与贡献" : "Contributing", link: `/${language}/contributing` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function integrationsSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] {
|
||||||
|
const zh = language === "zh";
|
||||||
|
return [{
|
||||||
|
text: zh ? "Agent 集成" : "Agent Integrations",
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: zh ? "集成总览" : "Overview", link: `/${language}/integrations` },
|
||||||
|
{ text: "Claude Code", link: `/${language}/integrations/claude-code` },
|
||||||
|
{ text: "Hermes Agent", link: `/${language}/integrations/hermes` },
|
||||||
|
{ text: "DeepSeek Harness", link: `/${language}/integrations/dsh` },
|
||||||
|
{ text: "OpenClaw", link: `/${language}/integrations/openclaw` },
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
function pluginsSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] {
|
||||||
|
const zh = language === "zh";
|
||||||
|
return [{
|
||||||
|
text: zh ? "插件" : "Plugins",
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: zh ? "插件管理" : "Plugin Management", link: `/${language}/plugin_management` },
|
||||||
|
{ text: zh ? "插件开发" : "Plugin Development", link: `/${language}/plugin_development` },
|
||||||
|
{ text: zh ? "每日论文" : "Daily Paper", link: `/${language}/plugins/daily-paper` },
|
||||||
|
{ text: "Auto Fin", link: `/${language}/plugins/auto-fin` },
|
||||||
|
{ text: "LME", link: `/${language}/plugins/lme` },
|
||||||
|
{ text: "BEAM", link: `/${language}/plugins/beam` },
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
function benchmarksSidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] {
|
||||||
|
const zh = language === "zh";
|
||||||
|
return [{
|
||||||
|
text: zh ? "记忆能力评测" : "Memory Benchmarks",
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: "LongMemEval", link: `/${language}/benchmarks/longmemeval` },
|
||||||
|
{ text: "BEAM", link: `/${language}/benchmarks/beam` },
|
||||||
|
{ text: "π-Bench", link: `/${language}/benchmarks/pibench` },
|
||||||
|
{ text: "Tool Memory / ExpG", link: `/${language}/benchmarks/toolmemory` },
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
function singlePageSidebar(language: "zh" | "en", page: "blog" | "faq"): DefaultTheme.SidebarItem[] {
|
||||||
|
const zh = language === "zh";
|
||||||
|
if (page === "blog") {
|
||||||
|
return [{
|
||||||
|
text: zh ? "ReMe 博客" : "ReMe Blog",
|
||||||
|
link: `/${language}/reme-blog`,
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: zh ? "ReMe介绍" : "About ReMe", link: `/${language}/reme-blog` },
|
||||||
|
{ text: zh ? "记忆标签" : "Memory Tags", link: `/${language}/blog_20260920` },
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return [{
|
||||||
|
text: zh ? "帮助" : "Help",
|
||||||
|
collapsed: false,
|
||||||
|
items: [{
|
||||||
|
text: zh ? "常见问题" : "Frequently Asked Questions",
|
||||||
|
link: `/${language}/faq`,
|
||||||
|
}],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
function sidebars(language: "zh" | "en"): DefaultTheme.SidebarMulti {
|
||||||
|
return {
|
||||||
|
[`/${language}/integrations`]: integrationsSidebar(language),
|
||||||
|
[`/${language}/workspace/`]: [],
|
||||||
|
[`/${language}/plugins/`]: pluginsSidebar(language),
|
||||||
|
[`/${language}/plugin_management`]: pluginsSidebar(language),
|
||||||
|
[`/${language}/plugin_development`]: pluginsSidebar(language),
|
||||||
|
[`/${language}/benchmarks/`]: benchmarksSidebar(language),
|
||||||
|
[`/${language}/reme-blog`]: singlePageSidebar(language, "blog"),
|
||||||
|
[`/${language}/blog_20260920`]: singlePageSidebar(language, "blog"),
|
||||||
|
[`/${language}/faq`]: singlePageSidebar(language, "faq"),
|
||||||
|
[`/${language}/`]: docsSidebar(language),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureRepositoryLinks(md: any) {
|
||||||
|
for (const ruleName of ["link_open", "image"] as const) {
|
||||||
|
const original = md.renderer.rules[ruleName];
|
||||||
|
md.renderer.rules[ruleName] = (tokens: any[], index: number, options: any, env: any, self: any) => {
|
||||||
|
const attribute = ruleName === "image" ? "src" : "href";
|
||||||
|
const token = tokens[index];
|
||||||
|
const attributeIndex = token.attrIndex(attribute);
|
||||||
|
const target = attributeIndex >= 0 ? token.attrs[attributeIndex][1] : "";
|
||||||
|
if (target && !/^(?:[a-z]+:|#|\/)/i.test(target)) {
|
||||||
|
const cleanTarget = target.split("#")[0].split("?")[0];
|
||||||
|
const generatedTarget = path.resolve(sourceRoot, path.dirname(env.relativePath), cleanTarget);
|
||||||
|
if (!fs.existsSync(generatedTarget)) {
|
||||||
|
const originalPage = sourcePathFor(env.relativePath);
|
||||||
|
const originalTarget = path.posix.normalize(path.posix.join(path.posix.dirname(originalPage), cleanTarget));
|
||||||
|
const suffix = target.slice(cleanTarget.length);
|
||||||
|
const url = ruleName === "image"
|
||||||
|
? `https://raw.githubusercontent.com/agentscope-ai/ReMe/main/${originalTarget}${suffix}`
|
||||||
|
: `${repository}/blob/main/${originalTarget}${suffix}`;
|
||||||
|
token.attrs[attributeIndex][1] = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return original ? original(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
lang: "zh-CN",
|
||||||
|
title: "ReMe",
|
||||||
|
description: "Local-first, file-native memory for agents",
|
||||||
|
base,
|
||||||
|
cleanUrls: true,
|
||||||
|
lastUpdated: true,
|
||||||
|
ignoreDeadLinks: [/^http:\/\/localhost(?::\d+)?(?:\/|$)/],
|
||||||
|
sitemap: {
|
||||||
|
hostname: "https://reme.agentscope.io",
|
||||||
|
transformItems(items) {
|
||||||
|
const isRoot = (url: string) => url.replace(/^\/+|\/+$/g, "") === "";
|
||||||
|
return items.filter((item) => !isRoot(item.url)).map((item) => {
|
||||||
|
const route = item.url.replace(/^\/+/, "");
|
||||||
|
const relativePath = !route || route.endsWith("/") ? `${route}index.md` : `${route}.md`;
|
||||||
|
const links = item.links?.filter((link) => !isRoot(link.url));
|
||||||
|
return { ...item, links, lastmod: sourceLastUpdated(relativePath) };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
head: [
|
||||||
|
["link", { rel: "icon", type: "image/svg+xml", href: `${base}reme-icon.svg` }],
|
||||||
|
["meta", { name: "theme-color", content: "#087f6a", media: "(prefers-color-scheme: light)" }],
|
||||||
|
["meta", { name: "theme-color", content: "#0d1512", media: "(prefers-color-scheme: dark)" }],
|
||||||
|
["script", {
|
||||||
|
defer: "",
|
||||||
|
src: "https://cloud.umami.is/script.js",
|
||||||
|
"data-website-id": "8cafe9df-d883-4046-b5e9-36dfd21a4884",
|
||||||
|
"data-domains": "reme.agentscope.io",
|
||||||
|
}],
|
||||||
|
["script", {}, legacyRedirectScript],
|
||||||
|
],
|
||||||
|
markdown: {
|
||||||
|
config: configureRepositoryLinks,
|
||||||
|
},
|
||||||
|
transformPageData(pageData, { siteConfig }) {
|
||||||
|
const sourcePath = path.join(siteConfig.srcDir, pageData.relativePath);
|
||||||
|
pageData.frontmatter._sourcePath = sourcePathFor(pageData.relativePath);
|
||||||
|
pageData.lastUpdated = sourceLastUpdated(pageData.relativePath);
|
||||||
|
try {
|
||||||
|
pageData.frontmatter._rawMarkdown = fs.readFileSync(sourcePath, "utf8");
|
||||||
|
} catch {
|
||||||
|
pageData.frontmatter._rawMarkdown = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildEnd(siteConfig) {
|
||||||
|
buildLlmsFiles(siteConfig.outDir);
|
||||||
|
},
|
||||||
|
themeConfig: {
|
||||||
|
logo: "/reme-icon.svg",
|
||||||
|
siteTitle: "ReMe",
|
||||||
|
nav: [
|
||||||
|
...nav("zh"),
|
||||||
|
{
|
||||||
|
text: "语言",
|
||||||
|
items: [
|
||||||
|
{ text: "简体中文", link: "/zh/" },
|
||||||
|
{ text: "English", link: "/en/" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outline: { label: "页面导航", level: [2, 3] },
|
||||||
|
search: {
|
||||||
|
provider: "local",
|
||||||
|
options: {
|
||||||
|
locales: {
|
||||||
|
zh: {
|
||||||
|
translations: {
|
||||||
|
button: { buttonText: "搜索文档", buttonAriaLabel: "搜索文档" },
|
||||||
|
modal: {
|
||||||
|
noResultsText: "没有找到相关内容",
|
||||||
|
resetButtonTitle: "清除查询",
|
||||||
|
footer: { selectText: "选择", navigateText: "切换", closeText: "关闭" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
socialLinks: [{ icon: "github", link: repository }],
|
||||||
|
footer: {
|
||||||
|
message: "Released under the Apache-2.0 License.",
|
||||||
|
copyright: "Copyright ReMe contributors",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
locales: {
|
||||||
|
zh: {
|
||||||
|
label: "简体中文",
|
||||||
|
lang: "zh-CN",
|
||||||
|
link: "/zh/",
|
||||||
|
themeConfig: {
|
||||||
|
nav: nav("zh"),
|
||||||
|
sidebar: sidebars("zh"),
|
||||||
|
outline: { label: "页面导航", level: [2, 3] },
|
||||||
|
docFooter: { prev: "上一页", next: "下一页" },
|
||||||
|
darkModeSwitchLabel: "外观",
|
||||||
|
sidebarMenuLabel: "菜单",
|
||||||
|
returnToTopLabel: "返回顶部",
|
||||||
|
langMenuLabel: "切换语言",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
label: "English",
|
||||||
|
lang: "en-US",
|
||||||
|
link: "/en/",
|
||||||
|
themeConfig: {
|
||||||
|
nav: nav("en"),
|
||||||
|
sidebar: sidebars("en"),
|
||||||
|
outline: { label: "On this page", level: [2, 3] },
|
||||||
|
docFooter: { prev: "Previous page", next: "Next page" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
47
docs/.vitepress/legacy-routes.mjs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
export const legacyRoutes = {
|
||||||
|
"readme-zh": "/zh/",
|
||||||
|
"readme-en": "/en/",
|
||||||
|
"zh-quick_start": "/zh/quick_start",
|
||||||
|
"en-quick_start": "/en/quick_start",
|
||||||
|
"zh-plugin_management": "/zh/plugin_management",
|
||||||
|
"en-plugin_management": "/en/plugin_management",
|
||||||
|
"zh-memory_as_file": "/zh/memory_as_file",
|
||||||
|
"en-memory_as_file": "/en/memory_as_file",
|
||||||
|
"zh-memory_search": "/zh/memory_search",
|
||||||
|
"en-memory_search": "/en/memory_search",
|
||||||
|
"zh-auto_memory": "/zh/auto_memory",
|
||||||
|
"en-auto_memory": "/en/auto_memory",
|
||||||
|
"zh-auto_resource": "/zh/auto_resource",
|
||||||
|
"en-auto_resource": "/en/auto_resource",
|
||||||
|
"zh-auto_link": "/zh/auto_link",
|
||||||
|
"en-auto_link": "/en/auto_link",
|
||||||
|
"zh-auto_dream": "/zh/auto_dream",
|
||||||
|
"en-auto_dream": "/en/auto_dream",
|
||||||
|
"zh-proactive": "/zh/proactive",
|
||||||
|
"en-proactive": "/en/proactive",
|
||||||
|
"zh-reme_scene": "/zh/reme_scene",
|
||||||
|
"en-reme_scene": "/en/reme_scene",
|
||||||
|
"zh-framework": "/zh/framework",
|
||||||
|
"en-framework": "/en/framework",
|
||||||
|
"zh-reme-blog": "/zh/reme-blog",
|
||||||
|
"en-reme-blog": "/en/reme-blog",
|
||||||
|
"zh-contributing": "/zh/contributing",
|
||||||
|
"en-contributing": "/en/contributing",
|
||||||
|
"typescript-zh": "/zh/integrations",
|
||||||
|
"typescript-en": "/en/integrations",
|
||||||
|
"studio-zh": "/zh/workspace/studio",
|
||||||
|
"studio-en": "/en/workspace/studio",
|
||||||
|
"daily-paper-zh": "/zh/plugins/daily-paper",
|
||||||
|
"daily-paper-en": "/en/plugins/daily-paper",
|
||||||
|
"auto-fin-zh": "/zh/plugins/auto-fin",
|
||||||
|
"auto-fin-en": "/en/plugins/auto-fin",
|
||||||
|
"beam-zh": "/zh/benchmarks/beam",
|
||||||
|
"beam-en": "/en/benchmarks/beam",
|
||||||
|
"longmemeval-zh": "/zh/benchmarks/longmemeval",
|
||||||
|
"longmemeval-en": "/en/benchmarks/longmemeval",
|
||||||
|
"pibench-zh": "/zh/benchmarks/pibench",
|
||||||
|
"pibench-en": "/en/benchmarks/pibench",
|
||||||
|
"toolmemory-zh": "/zh/benchmarks/toolmemory",
|
||||||
|
"toolmemory-en": "/en/benchmarks/toolmemory",
|
||||||
|
"agents-guide": "https://github.com/agentscope-ai/ReMe/blob/main/AGENTS.md",
|
||||||
|
};
|
||||||
32
docs/.vitepress/theme/CopyMarkdownButton.vue
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { useData } from "vitepress";
|
||||||
|
|
||||||
|
const { frontmatter, lang } = useData();
|
||||||
|
const copied = ref(false);
|
||||||
|
const label = computed(() => {
|
||||||
|
if (copied.value) return lang.value.startsWith("zh") ? "已复制" : "Copied";
|
||||||
|
return lang.value.startsWith("zh") ? "复制 Markdown" : "Copy Markdown";
|
||||||
|
});
|
||||||
|
|
||||||
|
async function copyMarkdown() {
|
||||||
|
const markdown = String(frontmatter.value._rawMarkdown || "");
|
||||||
|
if (!markdown) return;
|
||||||
|
await navigator.clipboard.writeText(markdown);
|
||||||
|
copied.value = true;
|
||||||
|
window.setTimeout(() => { copied.value = false; }, 1800);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="copy-markdown-wrap">
|
||||||
|
<button class="copy-markdown" type="button" :class="{ copied }" @click="copyMarkdown">
|
||||||
|
<svg v-if="!copied" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<rect x="9" y="9" width="13" height="13" rx="2" />
|
||||||
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
||||||
|
</svg>
|
||||||
|
<svg v-else viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6" /></svg>
|
||||||
|
{{ label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
523
docs/.vitepress/theme/HomePage.vue
Normal file
|
|
@ -0,0 +1,523 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, reactive } from "vue";
|
||||||
|
import { useData, withBase } from "vitepress";
|
||||||
|
|
||||||
|
const props = defineProps<{ lang: "zh" | "en" }>();
|
||||||
|
|
||||||
|
const repository = "https://github.com/agentscope-ai/ReMe";
|
||||||
|
const trafficShareBase = "https://cloud.umami.is/analytics/us/share/S1OZK1PSDLEpyiU5?date=30day&page=1";
|
||||||
|
const { isDark } = useData();
|
||||||
|
const stats = reactive({ stars: "3.4K+", forks: "293" });
|
||||||
|
|
||||||
|
const translations = {
|
||||||
|
zh: {
|
||||||
|
eyebrow: "LOCAL-FIRST · FILE-NATIVE",
|
||||||
|
title: "让 Agent 真正记住,\n让记忆始终属于你。",
|
||||||
|
lead: "ReMe 将对话、资料与经验沉淀为可读、可编辑、可检索、相互链接的本地文件,让不同 Agent 共享同一套长期记忆。",
|
||||||
|
quickStart: "快速开始",
|
||||||
|
learnMore: "了解 ReMe",
|
||||||
|
stars: "GitHub Stars",
|
||||||
|
forks: "Forks",
|
||||||
|
mapLabel: "OPEN ECOSYSTEM",
|
||||||
|
mapTitle: "ReMe 与开源生态",
|
||||||
|
capabilityTitle: "核心能力",
|
||||||
|
capabilities: [
|
||||||
|
{ mark: "▣", title: "文件即记忆", href: "/zh/memory_as_file", tone: "mint" },
|
||||||
|
{ mark: "✦", title: "自动记忆", href: "/zh/auto_memory", tone: "cyan" },
|
||||||
|
{ mark: "⌕", title: "混合检索", href: "/zh/memory_search", tone: "blue" },
|
||||||
|
{ mark: "⌁", title: "图谱关联", href: "/zh/auto_link", tone: "amber" },
|
||||||
|
],
|
||||||
|
agentTitle: "ReMe 接入 Agent",
|
||||||
|
agentGuide: "接入指南",
|
||||||
|
backendTitle: "检索引擎接入 ReMe",
|
||||||
|
backendGuide: "配置指南",
|
||||||
|
backendNote: "可选向量索引",
|
||||||
|
integrations: [
|
||||||
|
{ logo: "/ecosystem/qwenpaw.png", title: "QwenPaw", href: "https://github.com/agentscope-ai/QwenPaw", tone: "blue" },
|
||||||
|
{ logo: "/ecosystem/deepseek-harness.svg", title: "DeepSeek Harness", href: "https://github.com/deepseek-ai/deepseek-harness", tone: "mint" },
|
||||||
|
{ logo: "/ecosystem/openclaw.svg", title: "OpenClaw", href: "https://github.com/openclaw/openclaw", tone: "cyan" },
|
||||||
|
{ logo: "/ecosystem/claude-code.png", title: "Claude Code", href: "https://github.com/anthropics/claude-code", tone: "violet" },
|
||||||
|
{ logo: "/ecosystem/hermes.svg", title: "Hermes Agent", href: "https://github.com/NousResearch/hermes-agent", tone: "amber" },
|
||||||
|
],
|
||||||
|
backends: [
|
||||||
|
{ logo: "/ecosystem/zvec.ico", title: "Zvec", href: "https://github.com/alibaba/zvec", tone: "mint" },
|
||||||
|
{ logo: "/ecosystem/faiss.png", title: "FAISS", href: "https://github.com/facebookresearch/faiss", tone: "blue" },
|
||||||
|
],
|
||||||
|
benchmarkLabel: "02 / BENCHMARKS",
|
||||||
|
benchmarkTitle: "用真实评测,\n验证长期记忆",
|
||||||
|
benchmarkLead: "从跨会话检索到百万级上下文,ReMe 用可复现的公开基准验证长期记忆。",
|
||||||
|
benchmarkAction: "查看全部评测",
|
||||||
|
benchmarkNote: "仓库已发布参考结果 · Agentic score",
|
||||||
|
benchmarks: [
|
||||||
|
{ name: "LongMemEval", setting: "500 题 · cleaned-s", score: 89.4 },
|
||||||
|
{ name: "BEAM 100K", setting: "20 cases · 400 题", score: 66.1 },
|
||||||
|
{ name: "BEAM 1M", setting: "35 cases · 700 题", score: 65.0 },
|
||||||
|
],
|
||||||
|
piLabel: "π-Bench 主动性",
|
||||||
|
piDetail: "5 类用户画像的平均 PROC 得分",
|
||||||
|
piDelta: "较 NanoBot +2.4%",
|
||||||
|
sectionLabel: "03 / PRODUCTS & PLUGINS",
|
||||||
|
sectionTitle: "从记忆工作区,到自动研究",
|
||||||
|
sectionLead: "三个完整入口,把 ReMe 用到真实工作流中。",
|
||||||
|
products: [
|
||||||
|
{ mark: "▣", label: "WORKSPACE", title: "ReMe Studio", detail: "在本地 Web 工作区中浏览、编辑、搜索记忆,并探索 wikilink 图谱。", href: "/studio/?lang=zh", tone: "mint" },
|
||||||
|
{ mark: "◌", label: "DISCOVER", title: "Daily Paper", detail: "筛选值得阅读的论文,分析 PDF,并生成文件化笔记与五分钟简报。", href: "/zh/plugins/daily-paper", tone: "cyan" },
|
||||||
|
{ mark: "↗", label: "RESEARCH", title: "Auto Fin", detail: "连接最新财联社新闻与本地历史记忆,生成带 wikilink 的研究报告。", href: "/zh/plugins/auto-fin", tone: "amber" },
|
||||||
|
],
|
||||||
|
trafficLabel: "04 / OPEN METRICS",
|
||||||
|
trafficTitle: "公开、透明的访问趋势",
|
||||||
|
trafficDetail: "最近 30 天的页面浏览量与访问趋势,由 Umami 提供匿名统计。",
|
||||||
|
trafficAction: "打开完整数据页",
|
||||||
|
trafficFrameTitle: "ReMe 最近 30 天访问数据",
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
eyebrow: "LOCAL-FIRST · FILE-NATIVE",
|
||||||
|
title: "Memory for AI agents.\nFiles that remain yours.",
|
||||||
|
lead: "ReMe turns conversations, resources, and experience into readable, editable, searchable, interconnected local files—a shared long-term memory layer for every agent.",
|
||||||
|
quickStart: "Quick Start",
|
||||||
|
learnMore: "Meet ReMe",
|
||||||
|
stars: "GitHub Stars",
|
||||||
|
forks: "Forks",
|
||||||
|
mapLabel: "OPEN ECOSYSTEM",
|
||||||
|
mapTitle: "ReMe and the open ecosystem",
|
||||||
|
capabilityTitle: "CORE CAPABILITIES",
|
||||||
|
capabilities: [
|
||||||
|
{ mark: "▣", title: "Memory as files", href: "/en/memory_as_file", tone: "mint" },
|
||||||
|
{ mark: "✦", title: "Auto memory", href: "/en/auto_memory", tone: "cyan" },
|
||||||
|
{ mark: "⌕", title: "Hybrid search", href: "/en/memory_search", tone: "blue" },
|
||||||
|
{ mark: "⌁", title: "Linked graph", href: "/en/auto_link", tone: "amber" },
|
||||||
|
],
|
||||||
|
agentTitle: "ReMe for agents",
|
||||||
|
agentGuide: "Integration guide",
|
||||||
|
backendTitle: "Retrieval for ReMe",
|
||||||
|
backendGuide: "Configuration guide",
|
||||||
|
backendNote: "Optional vector indexes",
|
||||||
|
integrations: [
|
||||||
|
{ logo: "/ecosystem/qwenpaw.png", title: "QwenPaw", href: "https://github.com/agentscope-ai/QwenPaw", tone: "blue" },
|
||||||
|
{ logo: "/ecosystem/deepseek-harness.svg", title: "DeepSeek Harness", href: "https://github.com/deepseek-ai/deepseek-harness", tone: "mint" },
|
||||||
|
{ logo: "/ecosystem/openclaw.svg", title: "OpenClaw", href: "https://github.com/openclaw/openclaw", tone: "cyan" },
|
||||||
|
{ logo: "/ecosystem/claude-code.png", title: "Claude Code", href: "https://github.com/anthropics/claude-code", tone: "violet" },
|
||||||
|
{ logo: "/ecosystem/hermes.svg", title: "Hermes Agent", href: "https://github.com/NousResearch/hermes-agent", tone: "amber" },
|
||||||
|
],
|
||||||
|
backends: [
|
||||||
|
{ logo: "/ecosystem/zvec.ico", title: "Zvec", href: "https://github.com/alibaba/zvec", tone: "mint" },
|
||||||
|
{ logo: "/ecosystem/faiss.png", title: "FAISS", href: "https://github.com/facebookresearch/faiss", tone: "blue" },
|
||||||
|
],
|
||||||
|
benchmarkLabel: "02 / BENCHMARKS",
|
||||||
|
benchmarkTitle: "Memory that holds up\nunder pressure",
|
||||||
|
benchmarkLead: "From cross-session retrieval to million-token context, ReMe validates long-term memory with reproducible public benchmarks.",
|
||||||
|
benchmarkAction: "Explore all benchmarks",
|
||||||
|
benchmarkNote: "Published reference runs · Agentic score",
|
||||||
|
benchmarks: [
|
||||||
|
{ name: "LongMemEval", setting: "500 questions · cleaned-s", score: 89.4 },
|
||||||
|
{ name: "BEAM 100K", setting: "20 cases · 400 questions", score: 66.1 },
|
||||||
|
{ name: "BEAM 1M", setting: "35 cases · 700 questions", score: 65.0 },
|
||||||
|
],
|
||||||
|
piLabel: "π-Bench proactivity",
|
||||||
|
piDetail: "Average PROC score across five personas",
|
||||||
|
piDelta: "+2.4% over NanoBot",
|
||||||
|
sectionLabel: "03 / PRODUCTS & PLUGINS",
|
||||||
|
sectionTitle: "From memory workspace to automated research",
|
||||||
|
sectionLead: "Three complete paths for putting ReMe into real workflows.",
|
||||||
|
products: [
|
||||||
|
{ mark: "▣", label: "WORKSPACE", title: "ReMe Studio", detail: "Browse, edit, and search memory in a local web workspace, then explore its wikilink graph.", href: "/studio/?lang=en", tone: "mint" },
|
||||||
|
{ mark: "◌", label: "DISCOVER", title: "Daily Paper", detail: "Select useful papers, analyze PDFs, and create file-native notes plus a five-minute brief.", href: "/en/plugins/daily-paper", tone: "cyan" },
|
||||||
|
{ mark: "↗", label: "RESEARCH", title: "Auto Fin", detail: "Connect recent CLS news with local memory to create traceable, wikilink-backed reports.", href: "/en/plugins/auto-fin", tone: "amber" },
|
||||||
|
],
|
||||||
|
trafficLabel: "04 / OPEN METRICS",
|
||||||
|
trafficTitle: "Public, transparent traffic",
|
||||||
|
trafficDetail: "Page views and traffic trends from the last 30 days, measured anonymously with Umami.",
|
||||||
|
trafficAction: "Open the full report",
|
||||||
|
trafficFrameTitle: "ReMe traffic for the last 30 days",
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const text = computed(() => translations[props.lang]);
|
||||||
|
const trafficShareUrl = computed(() => `${trafficShareBase}&theme=${isDark.value ? "dark" : "light"}`);
|
||||||
|
const localLink = (href: string) => withBase(href);
|
||||||
|
|
||||||
|
function normalizeCompactCount(value: string) {
|
||||||
|
const normalized = value.trim().toUpperCase();
|
||||||
|
return /[KMB]$/.test(normalized) ? `${normalized}+` : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readBadge(metric: "stars" | "forks") {
|
||||||
|
const response = await fetch(`https://img.shields.io/github/${metric}/agentscope-ai/ReMe.json`);
|
||||||
|
if (!response.ok) throw new Error(`Unable to load ${metric}`);
|
||||||
|
const payload = await response.json();
|
||||||
|
return normalizeCompactCount(String(payload.message || payload.value || ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const [stars, forks] = await Promise.allSettled([readBadge("stars"), readBadge("forks")]);
|
||||||
|
if (stars.status === "fulfilled" && stars.value) stats.stars = stars.value;
|
||||||
|
if (forks.status === "fulfilled" && forks.value) stats.forks = forks.value;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="reme-home" :class="{ 'is-zh': lang === 'zh' }">
|
||||||
|
<section class="home-stage">
|
||||||
|
<div class="hero-copy">
|
||||||
|
<p class="eyebrow">{{ text.eyebrow }}</p>
|
||||||
|
<h1>{{ text.title }}</h1>
|
||||||
|
<p class="hero-lead">{{ text.lead }}</p>
|
||||||
|
|
||||||
|
<div class="hero-actions">
|
||||||
|
<a class="action primary" :href="localLink(`/${lang}/quick_start`)">{{ text.quickStart }} <span>→</span></a>
|
||||||
|
<a class="action secondary" :href="localLink(`/${lang}/overview`)">{{ text.learnMore }} <span>↗</span></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="repo-stats" aria-live="polite">
|
||||||
|
<a :href="`${repository}/stargazers`" target="_blank" rel="noreferrer" :aria-label="`${stats.stars} ${text.stars}`">
|
||||||
|
<span class="stat-icon">☆</span>
|
||||||
|
<span><strong>{{ stats.stars }}</strong><small>{{ text.stars }}</small></span>
|
||||||
|
</a>
|
||||||
|
<a :href="`${repository}/forks`" target="_blank" rel="noreferrer" :aria-label="`${stats.forks} ${text.forks}`">
|
||||||
|
<span class="stat-icon fork-icon">⑂</span>
|
||||||
|
<span><strong>{{ stats.forks }}</strong><small>{{ text.forks }}</small></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ecosystem-map">
|
||||||
|
<div class="map-heading">
|
||||||
|
<div>
|
||||||
|
<span>{{ text.mapLabel }}</span>
|
||||||
|
<strong>{{ text.mapTitle }}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ecosystem-network">
|
||||||
|
<div class="network-brands">
|
||||||
|
<div class="network-group-label">
|
||||||
|
<strong>{{ text.agentTitle }}</strong>
|
||||||
|
<a :href="localLink(`/${lang}/integrations`)">{{ text.agentGuide }} ↗</a>
|
||||||
|
</div>
|
||||||
|
<div class="brand-viewport">
|
||||||
|
<div class="brand-reel">
|
||||||
|
<a
|
||||||
|
v-for="integration in text.integrations"
|
||||||
|
:key="integration.title"
|
||||||
|
class="brand-link"
|
||||||
|
:class="integration.tone"
|
||||||
|
:href="integration.href"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
:aria-label="`${integration.title} GitHub`"
|
||||||
|
>
|
||||||
|
<span class="brand-mark" aria-hidden="true"><img :src="localLink(integration.logo)" alt="" /></span>
|
||||||
|
<strong>{{ integration.title }}</strong>
|
||||||
|
</a>
|
||||||
|
<div v-for="integration in text.integrations" :key="`${integration.title}-clone`" class="brand-link reel-clone" :class="integration.tone" aria-hidden="true">
|
||||||
|
<span class="brand-mark"><img :src="localLink(integration.logo)" alt="" /></span>
|
||||||
|
<strong>{{ integration.title }}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="network-group-label backend-label">
|
||||||
|
<strong>{{ text.backendTitle }}</strong>
|
||||||
|
<a :href="localLink(`/${lang}/memory_search#${lang === 'zh' ? '向量索引后端' : 'vector-index-backends'}`)">{{ text.backendGuide }} ↗</a>
|
||||||
|
</div>
|
||||||
|
<div class="backend-links">
|
||||||
|
<a v-for="backend in text.backends" :key="backend.title" class="brand-link" :class="backend.tone" :href="backend.href" target="_blank" rel="noopener noreferrer" :aria-label="`${backend.title} GitHub`">
|
||||||
|
<span class="brand-mark" aria-hidden="true"><img :src="localLink(backend.logo)" alt="" /></span>
|
||||||
|
<strong>{{ backend.title }}</strong>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="network-center" aria-hidden="true">
|
||||||
|
<span class="network-ring"></span>
|
||||||
|
<span class="network-core"><img :src="localLink('/reme-icon.svg')" alt="" /></span>
|
||||||
|
<strong>ReMe</strong>
|
||||||
|
</div>
|
||||||
|
<div class="network-capabilities">
|
||||||
|
<strong class="capability-label">{{ text.capabilityTitle }}</strong>
|
||||||
|
<a v-for="capability in text.capabilities" :key="capability.title" class="capability-link" :class="capability.tone" :href="localLink(capability.href)">
|
||||||
|
<span aria-hidden="true">{{ capability.mark }}</span>
|
||||||
|
<strong>{{ capability.title }}</strong>
|
||||||
|
<span aria-hidden="true">↗</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="benchmark-section page-panel">
|
||||||
|
<div class="benchmark-intro">
|
||||||
|
<p class="section-label">{{ text.benchmarkLabel }}</p>
|
||||||
|
<h2>{{ text.benchmarkTitle }}</h2>
|
||||||
|
<p>{{ text.benchmarkLead }}</p>
|
||||||
|
<a :href="localLink(`/${lang}/benchmarks/longmemeval`)">{{ text.benchmarkAction }} <span>→</span></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="benchmark-board">
|
||||||
|
<div class="benchmark-board-head">
|
||||||
|
<span>{{ text.benchmarkNote }}</span>
|
||||||
|
<span>0—100%</span>
|
||||||
|
</div>
|
||||||
|
<div class="benchmark-chart">
|
||||||
|
<div v-for="benchmark in text.benchmarks" :key="benchmark.name" class="benchmark-row">
|
||||||
|
<div class="benchmark-name">
|
||||||
|
<strong>{{ benchmark.name }}</strong>
|
||||||
|
<small>{{ benchmark.setting }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="benchmark-track" aria-hidden="true">
|
||||||
|
<span :style="{ width: `${benchmark.score}%` }"></span>
|
||||||
|
</div>
|
||||||
|
<strong class="benchmark-score">{{ benchmark.score.toFixed(1) }}%</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="pi-score" :href="localLink(`/${lang}/benchmarks/pibench`)" :aria-label="`${text.piLabel}: 0.580`">
|
||||||
|
<span class="pi-symbol">π</span>
|
||||||
|
<span>
|
||||||
|
<small>{{ text.piLabel }}</small>
|
||||||
|
<strong>0.580</strong>
|
||||||
|
<em>{{ text.piDetail }}</em>
|
||||||
|
</span>
|
||||||
|
<b>{{ text.piDelta }} ↗</b>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="product-section">
|
||||||
|
<div class="section-heading">
|
||||||
|
<p class="section-label">{{ text.sectionLabel }}</p>
|
||||||
|
<div>
|
||||||
|
<h2>{{ text.sectionTitle }}</h2>
|
||||||
|
<p>{{ text.sectionLead }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="product-grid">
|
||||||
|
<a
|
||||||
|
v-for="product in text.products"
|
||||||
|
:key="product.title"
|
||||||
|
class="product-card"
|
||||||
|
:class="product.tone"
|
||||||
|
:href="localLink(product.href)"
|
||||||
|
:target="product.href.startsWith('/studio/') ? '_self' : undefined"
|
||||||
|
>
|
||||||
|
<span class="product-mark">{{ product.mark }}</span>
|
||||||
|
<span class="product-label">{{ product.label }}</span>
|
||||||
|
<strong>{{ product.title }}</strong>
|
||||||
|
<p>{{ product.detail }}</p>
|
||||||
|
<span class="product-arrow">→</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="traffic-section page-panel">
|
||||||
|
<div class="traffic-heading">
|
||||||
|
<p class="section-label">{{ text.trafficLabel }}</p>
|
||||||
|
<h2>{{ text.trafficTitle }}</h2>
|
||||||
|
<p>{{ text.trafficDetail }}</p>
|
||||||
|
<a :href="localLink(`/${lang}/traffic`)">{{ text.trafficAction }} <span>→</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="traffic-window">
|
||||||
|
<div class="traffic-window-bar" aria-hidden="true">
|
||||||
|
<span></span><span></span><span></span><b>reme.agentscope.io · 30 days</b>
|
||||||
|
</div>
|
||||||
|
<iframe :src="trafficShareUrl" :title="text.trafficFrameTitle" loading="lazy" referrerpolicy="no-referrer" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.reme-home {
|
||||||
|
--home-ink: #17231e;
|
||||||
|
--home-muted: #66736d;
|
||||||
|
--home-line: #d8e2dc;
|
||||||
|
--home-accent: #087f6a;
|
||||||
|
--home-surface: #ffffff;
|
||||||
|
--home-surface-soft: #f7f9f7;
|
||||||
|
--home-glass: rgba(255, 255, 255, 0.72);
|
||||||
|
--home-tile: rgba(255, 255, 255, 0.88);
|
||||||
|
--home-primary-bg: #17241e;
|
||||||
|
--home-primary-text: #ffffff;
|
||||||
|
--home-shadow: rgba(28, 57, 45, 0.12);
|
||||||
|
--section-light: #f8faf8;
|
||||||
|
--section-tint: #edf4f1;
|
||||||
|
max-width: 1720px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 clamp(24px, 4.5vw, 72px) 80px;
|
||||||
|
color: var(--home-ink);
|
||||||
|
}
|
||||||
|
.home-stage {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 650px;
|
||||||
|
gap: clamp(42px, 4vw, 68px);
|
||||||
|
align-items: center;
|
||||||
|
min-height: calc(100vh - 64px);
|
||||||
|
padding: 72px 0 82px;
|
||||||
|
}
|
||||||
|
.home-stage::before {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
inset: 0 calc(50% - 50vw);
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse 70% 105% at -8% 18%, rgba(21, 158, 126, 0.14), transparent 72%),
|
||||||
|
radial-gradient(ellipse 68% 105% at 108% 10%, rgba(77, 103, 211, 0.13), transparent 73%),
|
||||||
|
linear-gradient(115deg, #f7fbf8 0%, #fbfaf6 49%, #f7f8fd 100%);
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
.eyebrow, .section-label { margin: 0; color: var(--home-accent); font: 750 13px/1.4 var(--vp-font-family-mono); letter-spacing: 0.16em; }
|
||||||
|
.hero-copy h1 { max-width: 100%; margin: 23px 0 0; color: var(--home-ink); font: 760 clamp(52px, 4.2vw, 76px)/1.04 Georgia, "Times New Roman", serif; white-space: pre-wrap; letter-spacing: -0.052em; }
|
||||||
|
.is-zh .hero-copy h1 { max-width: 760px; font-size: clamp(52px, 3.6vw, 64px); white-space: pre-line; word-break: keep-all; }
|
||||||
|
.hero-lead { max-width: 650px; margin: 28px 0 0; color: var(--home-muted); font-size: clamp(17px, 1.3vw, 20px); line-height: 1.75; }
|
||||||
|
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 34px; }
|
||||||
|
.action { display: inline-flex; align-items: center; justify-content: space-between; gap: 28px; min-width: 166px; min-height: 54px; padding: 0 19px; border: 1px solid var(--home-line); border-radius: 12px; color: var(--home-ink); background: var(--home-glass); text-decoration: none; font-weight: 720; box-shadow: 0 8px 22px color-mix(in srgb, var(--home-shadow) 50%, transparent); transition: transform 160ms ease, box-shadow 160ms ease; }
|
||||||
|
.action.primary { border-color: var(--home-primary-bg); color: var(--home-primary-text); background: var(--home-primary-bg); box-shadow: 0 12px 26px color-mix(in srgb, var(--home-primary-bg) 28%, transparent); }
|
||||||
|
.action:hover { transform: translateY(-2px); box-shadow: 0 15px 28px var(--home-shadow); }
|
||||||
|
.repo-stats { display: flex; flex-wrap: wrap; gap: 34px; margin-top: 40px; }
|
||||||
|
.repo-stats a { display: flex; gap: 12px; align-items: flex-start; color: inherit; text-decoration: none; }
|
||||||
|
.stat-icon { color: var(--home-accent); font-size: 30px; line-height: 1; }
|
||||||
|
.fork-icon { transform: rotate(90deg); }
|
||||||
|
.repo-stats strong { display: block; font: 740 28px/1 var(--vp-font-family-mono); letter-spacing: -0.04em; }
|
||||||
|
.repo-stats small { display: block; margin-top: 8px; color: var(--home-muted); font-size: 13px; }
|
||||||
|
.ecosystem-map { position: relative; min-width: 0; }
|
||||||
|
.map-heading { margin-bottom: 25px; }
|
||||||
|
.map-heading > div { display: flex; min-width: 0; flex-direction: column; gap: 8px; }
|
||||||
|
.map-heading span { color: var(--home-accent); font: 700 10px/1.4 var(--vp-font-family-mono); letter-spacing: 0.12em; }
|
||||||
|
.map-heading strong { font-size: 18px; line-height: 1.3; white-space: nowrap; }
|
||||||
|
.ecosystem-network { position: relative; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(100px, 0.62fr) minmax(0, 1fr); gap: 12px; align-items: center; min-height: 370px; }
|
||||||
|
.ecosystem-network::before, .ecosystem-network::after { position: absolute; z-index: 0; top: 50%; width: 19%; border-top: 1px dashed color-mix(in srgb, var(--home-accent) 58%, var(--home-line)); content: ""; }
|
||||||
|
.ecosystem-network::before { left: 30%; }.ecosystem-network::after { right: 27%; }
|
||||||
|
.network-brands, .network-center, .network-capabilities { position: relative; z-index: 1; min-width: 0; }
|
||||||
|
.network-group-label { display: flex; align-items: baseline; justify-content: space-between; gap: 5px; margin-bottom: 7px; }
|
||||||
|
.network-group-label strong, .capability-label { color: var(--home-muted); font: 750 10px/1.3 var(--vp-font-family-mono); letter-spacing: 0.04em; }
|
||||||
|
.network-group-label a { flex: none; color: var(--home-accent); font-size: 10px; font-weight: 700; text-decoration: none; white-space: nowrap; }
|
||||||
|
.network-group-label a:hover { text-decoration: underline; }
|
||||||
|
.brand-viewport { height: 184px; overflow: hidden; mask-image: linear-gradient(transparent, #000 12%, #000 88%, transparent); }
|
||||||
|
.brand-reel { display: grid; grid-auto-rows: 46px; gap: 6px; animation: brand-scroll 19s linear infinite; }
|
||||||
|
.brand-viewport:hover .brand-reel { animation-play-state: paused; }
|
||||||
|
.brand-viewport:focus-within { overflow-y: auto; mask-image: none; }
|
||||||
|
.brand-viewport:focus-within .brand-reel { animation: none; }
|
||||||
|
.brand-link { display: flex; min-width: 0; height: 46px; align-items: center; gap: 7px; padding: 5px; border: 1px solid color-mix(in srgb, var(--card-accent) 26%, var(--home-line)); border-radius: 9px; color: var(--home-ink); background: var(--home-tile); text-decoration: none; transition: border-color 160ms ease, transform 160ms ease; }
|
||||||
|
.brand-link:hover { border-color: var(--card-accent); transform: translateX(2px); }
|
||||||
|
.brand-mark { display: grid; width: 32px; height: 32px; flex: none; place-items: center; overflow: hidden; border: 1px solid var(--home-line); border-radius: 7px; background: #fff; }
|
||||||
|
.brand-mark img { width: 27px; height: 27px; margin: 0; object-fit: contain; }
|
||||||
|
.brand-link strong { min-width: 0; font-size: 13px; line-height: 1.15; overflow-wrap: anywhere; }
|
||||||
|
.reel-clone { pointer-events: none; }
|
||||||
|
.backend-label { margin-top: 13px; }
|
||||||
|
.backend-links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px; }
|
||||||
|
.backend-links .brand-link { height: 42px; gap: 5px; }
|
||||||
|
.backend-links .brand-mark { width: 28px; height: 28px; }
|
||||||
|
.backend-links .brand-mark img { width: 23px; height: 23px; }
|
||||||
|
.network-center { display: flex; min-height: 164px; flex-direction: column; align-items: center; justify-content: center; gap: 16px; }
|
||||||
|
.network-ring { position: absolute; top: 50%; left: 50%; width: 108px; height: 108px; border: 1px solid color-mix(in srgb, var(--home-accent) 30%, var(--home-line)); border-radius: 50%; transform: translate(-50%, -64%); animation: hub-pulse 3.6s ease-in-out infinite; }
|
||||||
|
.network-ring::after { position: absolute; inset: 10px; border: 1px solid var(--home-line); border-radius: 50%; content: ""; }
|
||||||
|
.network-core { z-index: 1; display: grid; width: 64px; height: 64px; place-items: center; border: 1px solid var(--home-line); border-radius: 50%; background: var(--home-surface); box-shadow: 0 10px 28px var(--home-shadow); }
|
||||||
|
.network-core img { width: 42px; height: 42px; margin: 0; }
|
||||||
|
.network-center strong { z-index: 1; color: var(--home-accent); font: 750 12px/1 var(--vp-font-family-mono); }
|
||||||
|
.network-capabilities { display: grid; gap: 8px; }
|
||||||
|
.capability-label { display: block; margin-bottom: 2px; }
|
||||||
|
.capability-link { display: flex; min-height: 53px; align-items: center; gap: 7px; padding: 7px; border: 1px solid color-mix(in srgb, var(--card-accent) 30%, var(--home-line)); border-radius: 10px; color: var(--home-ink); background: radial-gradient(circle at 100% 0, color-mix(in srgb, var(--card-accent) 11%, transparent), transparent 70%), var(--home-tile); text-decoration: none; transition: transform 160ms ease, border-color 160ms ease; }
|
||||||
|
.capability-link:hover { border-color: var(--card-accent); transform: translateX(2px); }
|
||||||
|
.capability-link span:first-child { display: grid; width: 27px; height: 27px; flex: none; place-items: center; border-radius: 7px; color: var(--card-accent); background: color-mix(in srgb, var(--card-accent) 12%, var(--home-surface)); font-size: 16px; }
|
||||||
|
.capability-link strong { min-width: 0; font-size: 13px; line-height: 1.2; }
|
||||||
|
.capability-link span:last-child { margin-left: auto; color: var(--card-accent); font-size: 13px; }
|
||||||
|
.brand-link:focus-visible, .capability-link:focus-visible, .network-group-label a:focus-visible { outline: 2px solid var(--home-accent); outline-offset: 2px; }
|
||||||
|
.mint { --card-accent: #059b7f; }.cyan { --card-accent: #169cc4; }.blue { --card-accent: #536bd8; }.amber { --card-accent: #c48324; }.violet { --card-accent: #7654c2; }
|
||||||
|
@keyframes brand-scroll { to { transform: translateY(-260px); } }
|
||||||
|
@keyframes hub-pulse { 50% { transform: translate(-50%, -64%) scale(1.12); opacity: 0.55; } }
|
||||||
|
.page-panel { position: relative; min-height: calc(100vh - 64px); }
|
||||||
|
.benchmark-section { display: grid; grid-template-columns: minmax(310px, 0.72fr) minmax(560px, 1.28fr); gap: clamp(54px, 7vw, 110px); align-items: center; padding: 112px 0 120px; color: var(--home-ink); }
|
||||||
|
.benchmark-section::before { position: absolute; z-index: -1; inset: 0 calc(50% - 50vw); border-top: 1px solid var(--home-line); background: var(--section-tint); content: ""; }
|
||||||
|
.benchmark-intro .section-label { color: var(--home-accent); }
|
||||||
|
.benchmark-intro h2 { max-width: 620px; margin: 23px 0 0; color: var(--home-ink); font-size: clamp(42px, 4.2vw, 68px); line-height: 1.06; white-space: pre-line; letter-spacing: -0.052em; }
|
||||||
|
.is-zh .benchmark-intro h2 { word-break: keep-all; }
|
||||||
|
.benchmark-intro > p:not(.section-label) { max-width: 560px; margin: 24px 0 0; color: var(--home-muted); font-size: 17px; line-height: 1.75; }
|
||||||
|
.benchmark-intro > a, .traffic-heading > a { display: inline-flex; align-items: center; gap: 32px; min-height: 50px; margin-top: 34px; padding: 0 18px; border: 1px solid color-mix(in srgb, var(--home-ink) 24%, transparent); border-radius: 11px; color: var(--home-ink); text-decoration: none; font-weight: 720; transition: background 160ms ease, transform 160ms ease; }
|
||||||
|
.benchmark-intro > a:hover, .traffic-heading > a:hover { background: color-mix(in srgb, var(--home-ink) 6%, transparent); transform: translateY(-2px); }
|
||||||
|
.benchmark-board { padding: clamp(24px, 3vw, 38px); border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 26px; color: white; background: #15352b; box-shadow: 0 28px 64px rgba(24, 58, 45, 0.2); }
|
||||||
|
.benchmark-board-head { display: flex; justify-content: space-between; gap: 20px; padding-bottom: 22px; border-bottom: 1px solid rgba(255, 255, 255, 0.13); color: #94aca3; font: 700 11px/1.4 var(--vp-font-family-mono); letter-spacing: 0.1em; text-transform: uppercase; }
|
||||||
|
.benchmark-chart { display: grid; gap: 28px; padding: 32px 0; }
|
||||||
|
.benchmark-row { display: grid; grid-template-columns: 150px minmax(140px, 1fr) 72px; gap: 20px; align-items: center; }
|
||||||
|
.benchmark-name strong { display: block; color: white; font-size: 16px; }
|
||||||
|
.benchmark-name small { display: block; margin-top: 5px; color: #8fa69d; font-size: 12px; }
|
||||||
|
.benchmark-track { height: 10px; overflow: hidden; border-radius: 99px; background: rgba(255, 255, 255, 0.09); }
|
||||||
|
.benchmark-track span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #38d3ae, #85ead2); box-shadow: 0 0 22px rgba(66, 220, 182, 0.28); }
|
||||||
|
.benchmark-score { color: white; font: 740 20px/1 var(--vp-font-family-mono); text-align: right; }
|
||||||
|
.pi-score { display: grid; grid-template-columns: 58px minmax(0, 1fr) auto; gap: 18px; align-items: center; padding: 20px; border: 1px solid rgba(142, 160, 255, 0.27); border-radius: 18px; color: white; background: linear-gradient(110deg, rgba(82, 105, 216, 0.23), rgba(82, 105, 216, 0.08)); text-decoration: none; }
|
||||||
|
.pi-symbol { display: grid; place-items: center; width: 58px; height: 58px; border-radius: 15px; color: #b9c6ff; background: rgba(107, 129, 236, 0.18); font: 700 31px/1 Georgia, serif; }
|
||||||
|
.pi-score small, .pi-score em { display: block; color: #aabbb4; font-size: 12px; font-style: normal; }
|
||||||
|
.pi-score strong { display: block; margin: 4px 0; font: 750 25px/1 var(--vp-font-family-mono); }
|
||||||
|
.pi-score b { color: #a9b7ff; font-size: 13px; white-space: nowrap; }
|
||||||
|
.product-section { position: relative; display: flex; min-height: calc(100vh - 64px); flex-direction: column; justify-content: center; padding: 112px 0 120px; }
|
||||||
|
.product-section::before { position: absolute; z-index: -1; inset: 0 calc(50% - 50vw); border-top: 1px solid var(--home-line); background: var(--section-light); content: ""; }
|
||||||
|
.section-heading { display: grid; grid-template-columns: minmax(190px, 0.38fr) minmax(0, 1fr); gap: 40px; align-items: start; margin-bottom: 36px; }
|
||||||
|
.section-heading h2, .traffic-heading h2 { max-width: 820px; margin: 0; color: var(--home-ink); font-size: clamp(38px, 3.6vw, 58px); line-height: 1.1; letter-spacing: -0.045em; }
|
||||||
|
.section-heading p:not(.section-label) { margin: 15px 0 0; color: var(--home-muted); font-size: 16px; }
|
||||||
|
.product-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
|
||||||
|
.product-card { position: relative; display: flex; min-height: 360px; flex-direction: column; padding: 30px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--card-accent) 30%, var(--home-line)); border-radius: 22px; color: var(--home-ink); background: radial-gradient(circle at 80% 0, color-mix(in srgb, var(--card-accent) 14%, transparent), transparent 50%), linear-gradient(150deg, var(--home-surface), color-mix(in srgb, var(--card-accent) 5%, var(--home-surface-soft))); text-decoration: none; transition: transform 170ms ease, box-shadow 170ms ease; }
|
||||||
|
.product-card:hover { transform: translateY(-4px); box-shadow: 0 20px 38px color-mix(in srgb, var(--card-accent) 14%, transparent); }
|
||||||
|
.product-mark { display: grid; place-items: center; width: 46px; height: 46px; border-radius: 13px; color: var(--card-accent); background: color-mix(in srgb, var(--card-accent) 13%, var(--home-surface)); font: 650 24px/1 var(--vp-font-family-mono); }
|
||||||
|
.product-label { margin-top: 50px; color: var(--card-accent); font: 720 11px/1.3 var(--vp-font-family-mono); letter-spacing: 0.12em; }
|
||||||
|
.product-card strong { margin-top: 10px; font-size: 23px; }
|
||||||
|
.product-card p { max-width: 390px; margin: 13px 0 0; color: var(--home-muted); font-size: 15px; line-height: 1.65; }
|
||||||
|
.product-arrow { position: absolute; right: 23px; bottom: 20px; color: var(--card-accent); font-size: 22px; }
|
||||||
|
.traffic-section { display: grid; grid-template-columns: minmax(300px, 0.64fr) minmax(600px, 1.36fr); gap: clamp(48px, 6vw, 94px); align-items: center; padding: 112px 0 116px; color: var(--home-ink); }
|
||||||
|
.traffic-section::before { position: absolute; z-index: -1; inset: 0 calc(50% - 50vw); border-top: 1px solid var(--home-line); background: var(--section-tint); content: ""; }
|
||||||
|
.traffic-heading .section-label { color: var(--home-accent); }
|
||||||
|
.traffic-heading h2 { margin-top: 22px; color: var(--home-ink); }
|
||||||
|
.traffic-heading > p:not(.section-label) { max-width: 460px; margin: 22px 0 0; color: var(--home-muted); font-size: 17px; line-height: 1.7; }
|
||||||
|
.traffic-window { overflow: hidden; height: min(650px, calc(100vh - 170px)); min-height: 520px; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 24px; background: white; box-shadow: 0 34px 80px rgba(0, 0, 0, 0.3); }
|
||||||
|
.traffic-window-bar { display: flex; align-items: center; gap: 8px; height: 46px; padding: 0 16px; border-bottom: 1px solid #e5e8e7; background: #f6f8f7; }
|
||||||
|
.traffic-window-bar span { width: 9px; height: 9px; border-radius: 50%; background: #a8b4af; }
|
||||||
|
.traffic-window-bar span:first-child { background: #f08d78; }
|
||||||
|
.traffic-window-bar span:nth-child(2) { background: #e6bf67; }
|
||||||
|
.traffic-window-bar span:nth-child(3) { background: #69bd9a; }
|
||||||
|
.traffic-window-bar b { margin-left: 8px; color: #7b8983; font: 650 11px/1 var(--vp-font-family-mono); }
|
||||||
|
.traffic-window iframe { width: 100%; height: calc(100% - 46px); border: 0; }
|
||||||
|
:global(html.dark .reme-home) { --home-ink: #edf7f3; --home-muted: #a8bbb3; --home-line: #2d4038; --home-accent: #57dfc3; --home-surface: #14201b; --home-surface-soft: #101a16; --home-glass: rgba(17, 28, 23, 0.78); --home-tile: rgba(20, 32, 27, 0.92); --home-primary-bg: #57dfc3; --home-primary-text: #07120e; --home-shadow: rgba(0, 0, 0, 0.3); --section-light: #0d1512; --section-tint: #14201b; color-scheme: dark; }
|
||||||
|
:global(html.dark .home-stage::before) { background: radial-gradient(ellipse 70% 105% at -8% 18%, rgba(24, 169, 143, 0.13), transparent 72%), radial-gradient(ellipse 68% 105% at 108% 10%, rgba(74, 100, 218, 0.15), transparent 73%), linear-gradient(115deg, #0d1713 0%, #101713 49%, #10131c 100%); }
|
||||||
|
:global(html.dark .benchmark-board) { background: #0b1712; box-shadow: 0 28px 64px rgba(0, 0, 0, 0.32); }
|
||||||
|
@media (max-width: 1680px) {
|
||||||
|
.home-stage { grid-template-columns: 1fr; min-height: auto; }
|
||||||
|
.hero-copy { max-width: 800px; padding-top: 26px; }
|
||||||
|
.hero-copy h1 { white-space: pre-wrap; }
|
||||||
|
.ecosystem-map { max-width: 850px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 1320px) {
|
||||||
|
.benchmark-section, .traffic-section { grid-template-columns: 1fr; min-height: auto; }
|
||||||
|
.benchmark-intro, .traffic-heading { max-width: 720px; }
|
||||||
|
.traffic-window { width: 100%; max-width: 1000px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.reme-home { padding-right: 20px; padding-left: 20px; }
|
||||||
|
.home-stage { gap: 42px; padding: 52px 0 62px; }
|
||||||
|
.hero-copy h1 { font-size: clamp(43px, 13vw, 62px); }
|
||||||
|
.hero-lead { font-size: 16px; }
|
||||||
|
.map-heading strong { white-space: normal; }
|
||||||
|
.section-heading { grid-template-columns: 1fr; gap: 18px; }
|
||||||
|
.product-grid { grid-template-columns: 1fr; }
|
||||||
|
.product-card { min-height: 280px; }
|
||||||
|
.benchmark-section, .product-section, .traffic-section { padding-top: 78px; padding-bottom: 84px; }
|
||||||
|
.benchmark-row { grid-template-columns: minmax(0, 1fr) auto; gap: 12px; }
|
||||||
|
.benchmark-track { grid-column: 1 / -1; grid-row: 2; }
|
||||||
|
.benchmark-score { grid-column: 2; grid-row: 1; }
|
||||||
|
.pi-score { grid-template-columns: 48px minmax(0, 1fr); }
|
||||||
|
.pi-symbol { width: 48px; height: 48px; }
|
||||||
|
.pi-score b { grid-column: 2; }
|
||||||
|
.traffic-window { height: 660px; min-height: 0; border-radius: 17px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 520px) {
|
||||||
|
.ecosystem-network { grid-template-columns: minmax(0, 1.1fr) 62px minmax(0, 1fr); gap: 4px; }
|
||||||
|
.network-group-label { flex-wrap: wrap; }
|
||||||
|
.brand-link strong, .capability-link strong { font-size: 10px; }
|
||||||
|
.network-ring { width: 78px; height: 78px; }
|
||||||
|
.network-core { width: 52px; height: 52px; }
|
||||||
|
.network-core img { width: 34px; height: 34px; }
|
||||||
|
.capability-link { gap: 3px; padding: 5px; }
|
||||||
|
.capability-link span:first-child { width: 22px; height: 22px; font-size: 13px; }
|
||||||
|
.backend-links { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.brand-viewport { height: auto; mask-image: none; }
|
||||||
|
.brand-reel, .network-ring { animation: none; }
|
||||||
|
.reel-clone { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
15
docs/.vitepress/theme/SourceLink.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useData } from "vitepress";
|
||||||
|
|
||||||
|
const { frontmatter, lang } = useData();
|
||||||
|
const sourcePath = computed(() => String(frontmatter.value._sourcePath || ""));
|
||||||
|
const label = computed(() => lang.value.startsWith("zh") ? "在 GitHub 查看源文件" : "View source on GitHub");
|
||||||
|
const href = computed(() => `https://github.com/agentscope-ai/ReMe/blob/main/${sourcePath.value}`);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="sourcePath" class="source-link-wrap">
|
||||||
|
<a :href="href" target="_blank" rel="noreferrer">{{ label }} ↗</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
55
docs/.vitepress/theme/TrafficPage.vue
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useData } from "vitepress";
|
||||||
|
|
||||||
|
const props = defineProps<{ lang: "zh" | "en" }>();
|
||||||
|
const shareBase = "https://cloud.umami.is/analytics/us/share/S1OZK1PSDLEpyiU5?date=30day&page=1";
|
||||||
|
const { isDark } = useData();
|
||||||
|
const shareUrl = computed(() => `${shareBase}&theme=${isDark.value ? "dark" : "light"}`);
|
||||||
|
const text = computed(() => props.lang === "zh" ? {
|
||||||
|
eyebrow: "OPEN METRICS",
|
||||||
|
title: "ReMe 访问数据",
|
||||||
|
description: "最近 30 天的页面浏览量与访问趋势,由 Umami 提供隐私友好的匿名统计。",
|
||||||
|
action: "在 Umami 中打开完整页面",
|
||||||
|
frameTitle: "ReMe 最近 30 天访问数据",
|
||||||
|
} : {
|
||||||
|
eyebrow: "OPEN METRICS",
|
||||||
|
title: "ReMe traffic",
|
||||||
|
description: "Page views and traffic trends from the last 30 days, measured anonymously with privacy-friendly Umami analytics.",
|
||||||
|
action: "Open the full report in Umami",
|
||||||
|
frameTitle: "ReMe traffic for the last 30 days",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main class="traffic-page">
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<p>{{ text.eyebrow }}</p>
|
||||||
|
<h1>{{ text.title }}</h1>
|
||||||
|
<span>{{ text.description }}</span>
|
||||||
|
</div>
|
||||||
|
<a :href="shareUrl" target="_blank" rel="noreferrer">{{ text.action }} ↗</a>
|
||||||
|
</header>
|
||||||
|
<div class="traffic-frame-wrap">
|
||||||
|
<iframe :src="shareUrl" :title="text.frameTitle" loading="eager" referrerpolicy="no-referrer" />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.traffic-page { max-width: 1440px; margin: 0 auto; padding: clamp(54px, 7vw, 96px) clamp(22px, 5vw, 74px) 90px; }
|
||||||
|
.traffic-page header { display: flex; align-items: flex-end; justify-content: space-between; gap: 40px; margin-bottom: 34px; }
|
||||||
|
.traffic-page header p { margin: 0 0 15px; color: var(--vp-c-brand-1); font: 750 12px/1.4 var(--vp-font-family-mono); letter-spacing: 0.16em; }
|
||||||
|
.traffic-page h1 { margin: 0; color: var(--vp-c-text-1); font-size: clamp(42px, 5.5vw, 68px); line-height: 1.05; letter-spacing: -0.05em; }
|
||||||
|
.traffic-page header span { display: block; max-width: 720px; margin-top: 17px; color: var(--vp-c-text-2); font-size: 17px; line-height: 1.65; }
|
||||||
|
.traffic-page header a { flex: none; padding: 11px 15px; border: 1px solid var(--vp-c-divider); border-radius: 10px; color: var(--vp-c-text-1); background: var(--vp-c-bg-soft); text-decoration: none; font-size: 14px; font-weight: 700; }
|
||||||
|
.traffic-page header a:hover { border-color: var(--vp-c-brand-1); color: var(--vp-c-brand-1); }
|
||||||
|
.traffic-frame-wrap { height: min(880px, calc(100vh - 210px)); min-height: 650px; overflow: hidden; border: 1px solid var(--vp-c-divider); border-radius: 20px; background: white; box-shadow: 0 24px 58px rgba(26, 62, 47, 0.12); }
|
||||||
|
.traffic-frame-wrap iframe { width: 100%; height: 100%; border: 0; }
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.traffic-page { padding-top: 42px; }
|
||||||
|
.traffic-page header { align-items: flex-start; flex-direction: column; gap: 20px; }
|
||||||
|
.traffic-frame-wrap { height: 720px; min-height: 0; border-radius: 14px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
370
docs/.vitepress/theme/custom.css
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
:root {
|
||||||
|
--vp-layout-max-width: 1560px;
|
||||||
|
--vp-c-brand-1: #087f6a;
|
||||||
|
--vp-c-brand-2: #086554;
|
||||||
|
--vp-c-brand-3: #19a98f;
|
||||||
|
--vp-c-brand-soft: rgba(8, 127, 106, 0.14);
|
||||||
|
--vp-c-bg: #ffffff;
|
||||||
|
--vp-c-bg-alt: #f4f7f5;
|
||||||
|
--vp-c-bg-elv: #ffffff;
|
||||||
|
--vp-c-bg-soft: #f1f6f3;
|
||||||
|
--vp-c-text-1: #17221d;
|
||||||
|
--vp-c-text-2: #526159;
|
||||||
|
--vp-c-text-3: #718078;
|
||||||
|
--vp-c-divider: #dce5e0;
|
||||||
|
--vp-font-family-base: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--vp-font-family-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||||
|
--reme-blue: #3156d9;
|
||||||
|
--reme-green: #087f6a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--vp-c-brand-1: #57dfc3;
|
||||||
|
--vp-c-brand-2: #35c6a9;
|
||||||
|
--vp-c-brand-3: #087f6a;
|
||||||
|
--vp-c-brand-soft: rgba(87, 223, 195, 0.14);
|
||||||
|
--vp-c-bg: #0d1512;
|
||||||
|
--vp-c-bg-alt: #09100d;
|
||||||
|
--vp-c-bg-elv: #14201b;
|
||||||
|
--vp-c-bg-soft: #17251f;
|
||||||
|
--vp-c-text-1: #edf7f3;
|
||||||
|
--vp-c-text-2: #bacbc4;
|
||||||
|
--vp-c-text-3: #91a49c;
|
||||||
|
--vp-c-divider: #283a33;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 8% 8%, rgba(25, 201, 176, 0.055), transparent 28rem),
|
||||||
|
var(--vp-c-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNav {
|
||||||
|
border-bottom: 1px solid color-mix(in srgb, var(--vp-c-divider) 84%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--vp-c-bg) 88%, transparent);
|
||||||
|
backdrop-filter: blur(18px) saturate(140%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBarTitle .logo {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBarTitle .title {
|
||||||
|
font-weight: 780;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBarSearch .DocSearch-Button,
|
||||||
|
.VPNavBarSearch button {
|
||||||
|
min-width: 190px;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--vp-c-bg-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.VPNavBar.has-sidebar .container > .title {
|
||||||
|
width: 113px !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar.has-sidebar .content {
|
||||||
|
padding-left: 113px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBarSearch {
|
||||||
|
padding-left: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1496px) {
|
||||||
|
.VPNavBar.has-sidebar .container {
|
||||||
|
position: relative !important;
|
||||||
|
max-width: 1496px !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar.has-sidebar .container > .title {
|
||||||
|
width: 81px !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar.has-sidebar .content {
|
||||||
|
padding-right: 0 !important;
|
||||||
|
padding-left: 81px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebar {
|
||||||
|
border-right: 1px solid var(--vp-c-divider);
|
||||||
|
background: color-mix(in srgb, var(--vp-c-bg-alt) 82%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebarItem .text {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebarItem.level-0 > .item > .text {
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
font-weight: 750;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebarItem.is-active > .item .link > .text {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPDocAsideOutline {
|
||||||
|
border-left-color: var(--vp-c-divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPDoc .container > .content {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPDoc .content-container {
|
||||||
|
max-width: 900px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc {
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.78;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1 {
|
||||||
|
margin-bottom: 28px;
|
||||||
|
font-size: clamp(36px, 5vw, 52px);
|
||||||
|
line-height: 1.08;
|
||||||
|
letter-spacing: -0.045em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h2 {
|
||||||
|
margin-top: 52px;
|
||||||
|
border-top-color: var(--vp-c-divider);
|
||||||
|
font-size: 27px;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h3 {
|
||||||
|
margin-top: 34px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc :not(pre) > code {
|
||||||
|
border-radius: 5px;
|
||||||
|
color: color-mix(in srgb, var(--vp-c-brand-1) 82%, var(--vp-c-text-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc div[class*="language-"] {
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--vp-c-brand-1) 62%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-markdown-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-markdown {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 9px;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-markdown:hover,
|
||||||
|
.copy-markdown.copied {
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-markdown svg {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-link-wrap {
|
||||||
|
margin-top: 48px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid var(--vp-c-divider);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-link-wrap a {
|
||||||
|
color: var(--vp-c-text-3);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-link-wrap a:hover {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHome {
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 14% 14%, rgba(25, 201, 176, 0.14), transparent 30rem),
|
||||||
|
radial-gradient(circle at 86% 10%, rgba(49, 86, 217, 0.11), transparent 28rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .name {
|
||||||
|
background: linear-gradient(120deg, var(--reme-green), var(--reme-blue));
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .name,
|
||||||
|
.VPHero .text {
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .image-bg {
|
||||||
|
width: min(88%, 420px);
|
||||||
|
height: 220px;
|
||||||
|
border-radius: 42%;
|
||||||
|
background: linear-gradient(125deg, rgba(25, 201, 176, 0.34), rgba(49, 86, 217, 0.25));
|
||||||
|
filter: blur(52px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .image-container {
|
||||||
|
isolation: isolate;
|
||||||
|
perspective: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .image-container::before,
|
||||||
|
.VPHero .image-container::after {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .image-container::before {
|
||||||
|
z-index: 0;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: min(88%, 430px);
|
||||||
|
height: 210px;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--vp-c-bg-elv) 64%, var(--reme-blue));
|
||||||
|
border-radius: 30px;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, color-mix(in srgb, var(--vp-c-bg-elv) 92%, transparent), color-mix(in srgb, var(--vp-c-bg-soft) 76%, transparent)),
|
||||||
|
radial-gradient(circle at 15% 15%, rgba(25, 201, 176, 0.14), transparent 42%);
|
||||||
|
box-shadow:
|
||||||
|
0 30px 70px rgba(19, 70, 91, 0.16),
|
||||||
|
inset 0 1px 0 color-mix(in srgb, white 72%, transparent);
|
||||||
|
backdrop-filter: blur(22px) saturate(135%);
|
||||||
|
transform: translate(-50%, -50%) rotate(-1.5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .image-container::after {
|
||||||
|
z-index: -1;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: min(78%, 380px);
|
||||||
|
height: 210px;
|
||||||
|
border: 1px solid rgba(49, 86, 217, 0.17);
|
||||||
|
border-radius: 30px;
|
||||||
|
background: linear-gradient(135deg, rgba(25, 201, 176, 0.1), rgba(49, 86, 217, 0.11));
|
||||||
|
transform: translate(-46%, -48%) rotate(7deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHero .image-src {
|
||||||
|
z-index: 1;
|
||||||
|
width: min(76%, 380px);
|
||||||
|
max-width: 380px !important;
|
||||||
|
max-height: 150px !important;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 12px 20px rgba(18, 78, 105, 0.16));
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .item:nth-child(1) { --feature-accent: #18b99e; }
|
||||||
|
.VPHomeFeatures .item:nth-child(2) { --feature-accent: #25a8dc; }
|
||||||
|
.VPHomeFeatures .item:nth-child(3) { --feature-accent: #6575e8; }
|
||||||
|
.VPHomeFeatures .item:nth-child(4) { --feature-accent: #e69a42; }
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature {
|
||||||
|
border-color: color-mix(in srgb, var(--feature-accent) 28%, var(--vp-c-divider));
|
||||||
|
border-radius: 16px;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 10% 4%, color-mix(in srgb, var(--feature-accent) 16%, transparent), transparent 52%),
|
||||||
|
linear-gradient(150deg, var(--vp-c-bg-elv), color-mix(in srgb, var(--feature-accent) 7%, var(--vp-c-bg-soft)));
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 color-mix(in srgb, white 76%, transparent),
|
||||||
|
0 8px 24px color-mix(in srgb, var(--feature-accent) 7%, transparent);
|
||||||
|
transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature .box {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
column-gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature .icon {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature .title {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
color: color-mix(in srgb, var(--feature-accent) 22%, var(--vp-c-text-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature .details {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 2;
|
||||||
|
align-self: start;
|
||||||
|
padding-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature .link-text {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPHomeFeatures .VPFeature:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
border-color: color-mix(in srgb, var(--feature-accent) 48%, var(--vp-c-divider));
|
||||||
|
box-shadow: 0 18px 38px color-mix(in srgb, var(--feature-accent) 15%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .VPHomeFeatures .VPFeature {
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 10% 4%, color-mix(in srgb, var(--feature-accent) 18%, transparent), transparent 54%),
|
||||||
|
linear-gradient(150deg, var(--vp-c-bg-elv), color-mix(in srgb, var(--feature-accent) 8%, var(--vp-c-bg-soft)));
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.vp-doc h1 { font-size: 34px; }
|
||||||
|
.vp-doc h2 { margin-top: 44px; font-size: 24px; }
|
||||||
|
.copy-markdown-wrap { margin-top: -8px; }
|
||||||
|
.VPHero .image-container::before,
|
||||||
|
.VPHero .image-container::after { height: 176px; border-radius: 24px; }
|
||||||
|
.VPHero .image-src { width: 72%; max-height: 120px !important; }
|
||||||
|
}
|
||||||
21
docs/.vitepress/theme/index.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { h } from "vue";
|
||||||
|
import DefaultTheme from "vitepress/theme";
|
||||||
|
import CopyMarkdownButton from "./CopyMarkdownButton.vue";
|
||||||
|
import HomePage from "./HomePage.vue";
|
||||||
|
import SourceLink from "./SourceLink.vue";
|
||||||
|
import TrafficPage from "./TrafficPage.vue";
|
||||||
|
import "./custom.css";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
extends: DefaultTheme,
|
||||||
|
enhanceApp({ app }) {
|
||||||
|
app.component("HomePage", HomePage);
|
||||||
|
app.component("TrafficPage", TrafficPage);
|
||||||
|
},
|
||||||
|
Layout() {
|
||||||
|
return h(DefaultTheme.Layout, null, {
|
||||||
|
"doc-before": () => h(CopyMarkdownButton),
|
||||||
|
"doc-after": () => h(SourceLink),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -2,16 +2,12 @@
|
||||||
|
|
||||||
`auto_dream` is ReMe's long-term memory distillation flow from daily to digest. By default it scans the target date and
|
`auto_dream` is ReMe's long-term memory distillation flow from daily to digest. By default it scans the target date and
|
||||||
the previous day, processes only files changed since the previous dream, extracts a small set of high-value memory units
|
the previous day, processes only files changed since the previous dream, extracts a small set of high-value memory units
|
||||||
across that window, integrates them into `digest/`, and writes the target day's `interests.yaml` for proactive use.
|
across that window, and integrates them into `digest/`.
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img src="../figure/auto-dream-and-proactive.svg" alt="ReMe Auto Dream and Proactive flow from daily to digest to proactive" width="92%">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
Its daily inputs usually come from [Auto Memory](./auto_memory.md) and [Auto Resource](./auto_resource.md). For the file
|
Its daily inputs usually come from [Auto Memory](./auto_memory.md) and [Auto Resource](./auto_resource.md). For the file
|
||||||
semantics of `digest/`, Sources sections, and wikilinks, see [Memory as File](./memory_as_file.md). For the linking
|
semantics of `digest/`, Sources sections, and wikilinks, see [Memory as File](./memory_as_file.md). For the linking
|
||||||
strategy used during Integrate, see [Auto Link](./auto_link.md). To read `interests.yaml`,
|
strategy used during Integrate, see [Auto Link](./auto_link.md). Proactive discovery is a separate flow; see
|
||||||
use [Proactive](./proactive.md).
|
[Proactive](./proactive.md).
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
@ -33,24 +29,15 @@ auto_dream:
|
||||||
max_units:
|
max_units:
|
||||||
type: integer
|
type: integer
|
||||||
default: 5
|
default: 5
|
||||||
topic_count:
|
|
||||||
type: integer
|
|
||||||
default: 3
|
|
||||||
topic_diversity_days:
|
|
||||||
type: integer
|
|
||||||
default: 7
|
|
||||||
steps:
|
steps:
|
||||||
- backend: dream_extract_step
|
- backend: dream_extract_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
topic_session_id: interests
|
|
||||||
scan_days: 2
|
scan_days: 2
|
||||||
max_units: 5
|
max_units: 5
|
||||||
- backend: dream_integrate_step
|
- backend: dream_integrate_step
|
||||||
- backend: dream_topics_step
|
|
||||||
topic_count: 3
|
|
||||||
topic_diversity_days: 7
|
|
||||||
- backend: dream_finish_step
|
- backend: dream_finish_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
|
- backend: auto_tag_step
|
||||||
```
|
```
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -61,8 +48,6 @@ Parameters:
|
||||||
| `hint` | Additional guidance from the caller for the Extract and Integrate stages. |
|
| `hint` | Additional guidance from the caller for the Extract and Integrate stages. |
|
||||||
| `scan_days` | Recent-date window ending at `date`; defaults to 2 and has a minimum of 1. |
|
| `scan_days` | Recent-date window ending at `date`; defaults to 2 and has a minimum of 1. |
|
||||||
| `max_units` | Maximum reusable units extracted in one run; defaults to 5. |
|
| `max_units` | Maximum reusable units extracted in one run; defaults to 5. |
|
||||||
| `topic_count` | Maximum number of topics written to `interests.yaml`. Defaults to 3. |
|
|
||||||
| `topic_diversity_days` | Number of past days of `interests.yaml` files considered when avoiding duplicate topics. Defaults to 7. |
|
|
||||||
|
|
||||||
## Inputs and Outputs
|
## Inputs and Outputs
|
||||||
|
|
||||||
|
|
@ -76,8 +61,7 @@ daily/2026-06-20.md
|
||||||
daily/2026-06-20/**/*.md
|
daily/2026-06-20/**/*.md
|
||||||
```
|
```
|
||||||
|
|
||||||
Every `daily/<date>/interests.yaml` in the scan window is excluded from extraction so previous proactive output cannot
|
Only Markdown day indexes and notes are scanned. Proactive state and `interests.yaml` are not Auto Dream inputs.
|
||||||
feed back into the next run. Final topics are written only for the target date.
|
|
||||||
|
|
||||||
The main outputs are:
|
The main outputs are:
|
||||||
|
|
||||||
|
|
@ -86,7 +70,6 @@ The main outputs are:
|
||||||
| `digest/procedure/*.md` | Methods, workflows, runbooks, and executable experience. |
|
| `digest/procedure/*.md` | Methods, workflows, runbooks, and executable experience. |
|
||||||
| `digest/personal/*.md` | User-, team-, and project-related preferences, facts, and long-term context. |
|
| `digest/personal/*.md` | User-, team-, and project-related preferences, facts, and long-term context. |
|
||||||
| `digest/wiki/*.md` | General knowledge, concepts, observations, and decision precedents. |
|
| `digest/wiki/*.md` | General knowledge, concepts, observations, and decision precedents. |
|
||||||
| `daily/<date>/interests.yaml` | Topics worth proactive attention from the host agent that day. |
|
|
||||||
| `metadata/file_catalog/dream*` | Dream-specific catalog used to detect changes in daily inputs. |
|
| `metadata/file_catalog/dream*` | Dream-specific catalog used to detect changes in daily inputs. |
|
||||||
|
|
||||||
## Four Stages
|
## Four Stages
|
||||||
|
|
@ -97,18 +80,15 @@ The main outputs are:
|
||||||
|
|
||||||
1. Refresh each `daily/<date>.md` in the scan window.
|
1. Refresh each `daily/<date>.md` in the scan window.
|
||||||
2. Scan those day indexes and `daily/<date>/**/*.md`, comparing mtimes with `file_catalog: dream`.
|
2. Scan those day indexes and `daily/<date>/**/*.md`, comparing mtimes with `file_catalog: dream`.
|
||||||
3. Send all changed files together to the LLM and globally extract two structured result types: `units` and `topics`.
|
3. Send all changed files together to the LLM and globally extract structured memory `units`.
|
||||||
|
|
||||||
`units` are long-term memory units ready to be distilled into digest. Each has `name`, `bucket`, `summary`, and `paths`.
|
`units` are long-term memory units ready to be distilled into digest. Each has `name`, `bucket`, `summary`, and `paths`.
|
||||||
A run returns at most `max_units`; extraction merges cross-file evidence for the same abstraction and drops passing
|
A run returns at most `max_units`; extraction merges cross-file evidence for the same abstraction and drops passing
|
||||||
mentions, per-file summaries, and weak candidates without reusable value. `bucket` may only be `procedure`, `personal`,
|
mentions, per-file summaries, and weak candidates without reusable value. `bucket` may only be `procedure`, `personal`,
|
||||||
or `wiki`; unknown values are routed to `wiki`.
|
or `wiki`; unknown values are routed to `wiki`.
|
||||||
|
|
||||||
`topics` are proactive-interest candidates for the day. They contain `title`, `reason`, `evidence`, `keywords`, and
|
If there are no changed files, Extract succeeds with no units; Integrate then has no unit work, and Finish still
|
||||||
`paths` and are filtered again in the Topics stage.
|
performs its normal catalog summary. If files changed but no LLM is
|
||||||
|
|
||||||
If there are no changed files, Extract succeeds with no units; Integrate then has no unit work, Topics preserves any
|
|
||||||
existing target-day topics, and Finish still performs its normal catalog summary. If files changed but no LLM is
|
|
||||||
configured, Extract fails because extraction requires an LLM.
|
configured, Extract fails because extraction requires an LLM.
|
||||||
|
|
||||||
### 2. Integrate
|
### 2. Integrate
|
||||||
|
|
@ -140,51 +120,32 @@ There are four integration actions:
|
||||||
Successfully integrated units are recorded in `integrate_results`. Failed units enter `failed_units`, and their source
|
Successfully integrated units are recorded in `integrate_results`. Failed units enter `failed_units`, and their source
|
||||||
paths enter `failed_paths`. The Finish stage does not checkpoint failed paths, ensuring that they can be retried later.
|
paths enter `failed_paths`. The Finish stage does not checkpoint failed paths, ensuring that they can be retried later.
|
||||||
|
|
||||||
### 3. Topics
|
### 3. Finish
|
||||||
|
|
||||||
`dream_topics_step` turns topic candidates from Extract into the final `daily/<date>/interests.yaml` for the day.
|
|
||||||
|
|
||||||
It reads:
|
|
||||||
|
|
||||||
```text
|
|
||||||
daily/<date>/interests.yaml
|
|
||||||
daily/<each of the previous topic_diversity_days dates>/interests.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
Existing topics from the same day are preserved, while similar topics from the previous `topic_diversity_days` days are
|
|
||||||
deduplicated. At most three topics are written by default. With an LLM configured, the LLM selects topics that are more
|
|
||||||
specific, actionable, and non-repetitive. Without an LLM, the step falls back to local normalization and deduplication.
|
|
||||||
|
|
||||||
Example output format. See [Proactive](./proactive.md) for the interface that reads this file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
date: 2026-06-20
|
|
||||||
topic_count: 3
|
|
||||||
diversity_days: 7
|
|
||||||
topics:
|
|
||||||
- title: Quality regression in the memory retrieval pipeline
|
|
||||||
reason: The user has recently made repeated changes to search, node_search, and dream integration.
|
|
||||||
evidence: daily/2026-06-20/session.md
|
|
||||||
keywords:
|
|
||||||
- memory search
|
|
||||||
- auto dream
|
|
||||||
paths:
|
|
||||||
- daily/2026-06-20/session.md
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Finish
|
|
||||||
|
|
||||||
`dream_finish_step` completes the run:
|
`dream_finish_step` completes the run:
|
||||||
|
|
||||||
1. Write successfully processed changed paths to `file_catalog: dream`.
|
1. Write successfully processed changed paths to `file_catalog: dream`.
|
||||||
2. Also write the target `daily/<date>/interests.yaml` and every refreshed day-index page in the scan window to the
|
2. Also write every refreshed day-index page in the scan window to the catalog.
|
||||||
catalog.
|
|
||||||
3. Persist the dream catalog if there were upserts or deletions.
|
3. Persist the dream catalog if there were upserts or deletions.
|
||||||
4. Return a summary containing counts for scanned, changed, integrated, topics, checkpoints, and related values.
|
4. Return a summary containing counts for scanned, changed, integrated, checkpoints, and related values.
|
||||||
|
|
||||||
|
Auto Dream neither reads nor writes proactive state or `interests.yaml`. Those files are owned by the proactive refresh
|
||||||
|
pipeline; see [Proactive](./proactive.md).
|
||||||
|
|
||||||
Failed paths are not checkpointed. The next `auto_dream` run therefore continues to treat them as changed inputs until
|
Failed paths are not checkpointed. The next `auto_dream` run therefore continues to treat them as changed inputs until
|
||||||
integration succeeds.
|
integration succeeds.
|
||||||
|
|
||||||
|
### 4. Auto Tag
|
||||||
|
|
||||||
|
After Finish, both `auto_dream` and `dream_cron` run `auto_tag_step` on Markdown digest files actually created or modified
|
||||||
|
during integration, including writes recovered after agent errors. Repeated writes to one file are tagged once. The
|
||||||
|
Step uses the same request-scoped `changes` contract as [Auto Memory](./auto_memory.md) and writes entity tags to the
|
||||||
|
configured frontmatter key, `memory_tags` by default. Unchanged files and daily source notes are not tagged by Dream.
|
||||||
|
|
||||||
|
Tagging diagnostics appear in `metadata.auto_tag`. Per-file tagging failures preserve the dream answer, success status,
|
||||||
|
and checkpoint decisions. A later run without file changes does not automatically retry failed tagging. Tag-index
|
||||||
|
updates follow the existing asynchronous file watcher.
|
||||||
|
|
||||||
## Running Auto Dream
|
## Running Auto Dream
|
||||||
|
|
||||||
CLI:
|
CLI:
|
||||||
|
|
@ -216,9 +177,9 @@ jobs:
|
||||||
- backend: dream_extract_step
|
- backend: dream_extract_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
- backend: dream_integrate_step
|
- backend: dream_integrate_step
|
||||||
- backend: dream_topics_step
|
|
||||||
- backend: dream_finish_step
|
- backend: dream_finish_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
|
- backend: auto_tag_step
|
||||||
```
|
```
|
||||||
|
|
||||||
## Important Boundaries
|
## Important Boundaries
|
||||||
|
|
@ -232,7 +193,6 @@ the workspace-relative wikilink semantics described in
|
||||||
[Memory as File](./memory_as_file.md).
|
[Memory as File](./memory_as_file.md).
|
||||||
|
|
||||||
`auto_dream` does not invent an overview from nothing. Only content that actually appears in daily input and is
|
`auto_dream` does not invent an overview from nothing. Only content that actually appears in daily input and is
|
||||||
extracted as a unit or topic can enter digest or `interests.yaml`.
|
extracted as a memory unit can enter digest.
|
||||||
|
|
||||||
The complete flow depends on an LLM for Extract and Integrate. Topics can perform local deduplication without an LLM,
|
The complete flow depends on an LLM for Extract, Integrate, and Auto Tag.
|
||||||
but that does not mean the full dream flow can run offline.
|
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ auto_dream:
|
||||||
steps:
|
steps:
|
||||||
- dream_extract_step
|
- dream_extract_step
|
||||||
- dream_integrate_step # where auto_link actually happens
|
- dream_integrate_step # where auto_link actually happens
|
||||||
- dream_topics_step
|
|
||||||
- dream_finish_step
|
- dream_finish_step
|
||||||
|
- auto_tag_step
|
||||||
```
|
```
|
||||||
|
|
||||||
The Integrate stage processes each unit independently. A unit is written to exactly one target digest node, but that
|
The Integrate stage processes each unit independently. A unit is written to exactly one target digest node, but that
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,64 @@ session/
|
||||||
Each daily note points to its corresponding conversation record. Saved messages omit tool-result blocks and base64 data
|
Each daily note points to its corresponding conversation record. Saved messages omit tool-result blocks and base64 data
|
||||||
blocks, preventing recalled memory and binary payloads from being mistaken for user-provided evidence later.
|
blocks, preventing recalled memory and binary payloads from being mistaken for user-provided evidence later.
|
||||||
|
|
||||||
|
## Images in Conversations
|
||||||
|
|
||||||
|
Auto Memory can read images together with the surrounding conversation. Images are disabled by default; enable them for a
|
||||||
|
call with `include_images=true`.
|
||||||
|
|
||||||
|
Image input requires an `agentscope` wrapper with a vision-capable `as_llm` model and compatible formatter.
|
||||||
|
Auto Memory uses that model to read the conversation, without generating captions first. When images are disabled or no
|
||||||
|
image blocks are present, the existing text-only behavior is unchanged, including support for other wrappers.
|
||||||
|
|
||||||
|
Pass images as top-level AgentScope `DataBlock` values in `messages`, with an `image/` media type. Text and images stay in
|
||||||
|
their original order, with speaker and timestamp boundaries preserved. Base64 sources and HTTP(S) URLs pass unchanged to
|
||||||
|
the formatter; images are not resized or transcoded. URLs are not downloaded and must be accessible to the model provider. For local
|
||||||
|
files, submit Base64 instead of a `file://` URL; other URL schemes are also unsupported.
|
||||||
|
|
||||||
|
With images enabled, Auto Memory saves each Base64 image's original bytes under the configured `session_dir`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
session/images/<session_id>/msg-<encoded-message-id>-image-<block-index>.<ext>
|
||||||
|
```
|
||||||
|
|
||||||
|
The filename uses the message's `id` and the image's position among all content blocks, starting at zero; the extension
|
||||||
|
comes from its media type. Keep session IDs, message IDs and block positions stable when resubmitting a conversation:
|
||||||
|
an existing file at that path is reused without comparing its contents. Use a new message ID when replacing an image.
|
||||||
|
Calls with images disabled or no images do not save attachments.
|
||||||
|
|
||||||
|
Each image is accompanied by its exact source link in the model input. The memory prompt asks the Agent to cite that source
|
||||||
|
beside the corresponding visual facts, for example:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
The diagram places Gateway before Worker and PostgreSQL. See [[session/images/session-a/msg-6d6573736167652d61-image-1.png]].
|
||||||
|
```
|
||||||
|
|
||||||
|
For URL images, the citation uses the original URL. Auto Memory also adds the supplied image sources to the daily note's
|
||||||
|
`source_images` frontmatter, preserving existing entries. This list records provenance; the body links connect individual
|
||||||
|
facts to their images. These session attachments are not watched as resources and do not trigger separate caption calls.
|
||||||
|
|
||||||
|
The wrapper's `context_config.max_image_num` limits the number of images per call; Auto Memory rejects excess images rather
|
||||||
|
than increasing the limit. The AgentScope default is 5. To use a higher limit, set it when starting the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start components.agent_wrapper.default.context_config.max_image_num=20
|
||||||
|
```
|
||||||
|
|
||||||
|
Then call the running service from another terminal, using the same workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme auto_memory session_id=session-a include_images=true messages='[...]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Model and formatter limits still apply. When image input is enabled and images are present, Auto Memory checks the wrapper
|
||||||
|
backend, URL schemes and image count before saving the conversation. Later formatter or provider errors are returned
|
||||||
|
without retrying as text-only. As with text-only calls, those errors do not roll back an already saved conversation.
|
||||||
|
|
||||||
|
Source JSONL saving follows the filtering rules above, including the omission of Base64 blocks. Saved attachments are not
|
||||||
|
automatically restored into a replay of that JSONL; to process the images again, resubmit the original messages. Attachments
|
||||||
|
already saved remain available if the model call fails or decides not to write a memory card; Auto Memory does not clean
|
||||||
|
them up automatically. Local source paths can also be passed to `read_image`.
|
||||||
|
|
||||||
## Message Timestamps
|
## Message Timestamps
|
||||||
|
|
||||||
Auto Memory preserves each retained message's `created_at` in both the prompt and the source conversation JSONL. When importing historical
|
Auto Memory preserves each retained message's `created_at` in both the prompt and the source conversation JSONL. When importing historical
|
||||||
|
|
@ -110,5 +168,13 @@ reme auto_memory \
|
||||||
|
|
||||||
## What Happens Next
|
## What Happens Next
|
||||||
|
|
||||||
|
The default `auto_memory` and `auto_memory_cc` jobs run `auto_tag_step` after recording memory. Only a daily note that
|
||||||
|
was actually created or modified is tagged, using its final path after any rename. Claude Code callers still pass only
|
||||||
|
`session_id`; repeated Stop events with no new messages skip both memory generation and tagging.
|
||||||
|
|
||||||
|
Tags describe the document's central entities and are stored in the configured frontmatter key (`memory_tags` by default).
|
||||||
|
Per-file tagging failures are reported in `metadata.auto_tag` while preserving the memory response. Calls without note
|
||||||
|
changes do not automatically retry failed tagging; the existing file watcher updates the tag index asynchronously.
|
||||||
|
|
||||||
Auto Memory only creates memory in the daily layer. To distill this material further into long-term `digest/` nodes, use
|
Auto Memory only creates memory in the daily layer. To distill this material further into long-term `digest/` nodes, use
|
||||||
[Auto Dream](./auto_dream.md). To search daily and digest content, use [Memory Search](./memory_search.md).
|
[Auto Dream](./auto_dream.md). To search daily and digest content, use [Memory Search](./memory_search.md).
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ In short, it turns "a file was archived" into "the resource is usable."
|
||||||
|
|
||||||
Auto Resource uses `resource/` as the entry point for source material. Date directories are recommended, and their date
|
Auto Resource uses `resource/` as the entry point for source material. Date directories are recommended, and their date
|
||||||
determines which daily memory layer receives the interpreted card. A file directly under `resource/` is also supported
|
determines which daily memory layer receives the interpreted card. A file directly under `resource/` is also supported
|
||||||
and uses today in the application timezone.
|
and uses today in the application timezone when it is first processed. On later days, an exact `source_resource` match
|
||||||
|
keeps updates and deletion tied to that original daily card instead of creating a new card or leaving an orphan.
|
||||||
|
|
||||||
Example directory:
|
Example directory:
|
||||||
|
|
||||||
|
|
@ -49,13 +50,77 @@ workspace/
|
||||||
meeting-notes.csv
|
meeting-notes.csv
|
||||||
```
|
```
|
||||||
|
|
||||||
The current Beta version is best suited to text-based resources such as `md`, `txt`, `json`, `jsonl`, `csv`, `yaml`, and
|
Text resources such as `md`, `txt`, `json`, `jsonl`, `csv`, `yaml`, and `html` are the primary fit. Image resources
|
||||||
`html`.
|
(`png`, `jpg`, `jpeg`, `webp`, `gif`, `bmp`, `tiff`, `heic`) produce caption cards as described in
|
||||||
|
[Image Resources](#image-resources).
|
||||||
|
|
||||||
|
Internally, one `AutoResourceStep` receives each change batch and sends every item to the first configured processor
|
||||||
|
whose class-level matcher accepts it. `AutoImageResourceStep` handles image suffixes and `AutoTextResourceStep` is the
|
||||||
|
final fallback. A new modality can therefore add a registered processor, its prompt, and one `dispatch_steps` entry
|
||||||
|
without changing the router.
|
||||||
|
|
||||||
|
## Image Resources
|
||||||
|
|
||||||
|
Text and image resources share the same agent-wrapper and note-writing tools. Image inputs add a native AgentScope
|
||||||
|
image block alongside the interpretation instructions; the agent writes a caption card linked to the original image.
|
||||||
|
The card body starts with an `![[resource/...]]` embed link and the frontmatter carries `kind: image` and `media_type`,
|
||||||
|
so text search reaches image content through the caption.
|
||||||
|
|
||||||
|
Image processing is enabled by default (`include_images=true`) and requires an AgentScope wrapper bound to a compatible
|
||||||
|
model and formatter. Configure the model through `components.agent_wrapper.<name>.as_llm`, selecting the wrapper with
|
||||||
|
`agent_wrapper` on the resource Step. The former image-Step `as_llm` override and automatic `as_llm.vision` selection
|
||||||
|
are replaced by that binding. There is no separate caption model, schema-extraction call, or text-only retry after an
|
||||||
|
agent failure. An agent workflow can make multiple model requests while using its tools.
|
||||||
|
|
||||||
|
Each image interpretation starts a new session. Use the returned `agent_session_id` to find its processing record;
|
||||||
|
reprocessing the same image still updates the original card. The body should contain the image embed followed by a
|
||||||
|
description or transcription under `## Caption`, not an empty caption or a JSON response. Leave `status` to later
|
||||||
|
processing steps and keep its existing value when updating the card.
|
||||||
|
|
||||||
|
Customize image instructions with `prompt_dict.resource_instructions` (`resource_instructions_zh` for Chinese).
|
||||||
|
Rename existing `user_message` / `user_message_zh` settings accordingly.
|
||||||
|
Shared create/update templates insert these instructions at `{resource_instructions}`; older templates
|
||||||
|
without the placeholder receive them at the end.
|
||||||
|
|
||||||
|
Set `include_images=false` on an `auto_resource` call or as a Job default to skip **all** image events, including
|
||||||
|
deletions. Call-time values override Job defaults; when neither is set, image processing is enabled. For the watcher, use
|
||||||
|
`jobs.resource_watch_loop.include_images=false`; for manual calls, use `jobs.auto_resource.include_images=false`.
|
||||||
|
The image processor reports each skip in the existing result and warning log; text processing is unchanged. Existing
|
||||||
|
image cards are left untouched, even if their source image is deleted. Re-enabling images does not replay skipped
|
||||||
|
events; explicitly submit the affected paths to `auto_resource` when compensation is needed. The wrapper's configured
|
||||||
|
image-count limit is respected and must allow at least one image per resource call; it is not increased automatically.
|
||||||
|
|
||||||
|
Configure the wrapper when starting the persistent service. For example, to allow one image per agent context:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start components.agent_wrapper.default.context_config.max_image_num=1
|
||||||
|
```
|
||||||
|
|
||||||
|
The watcher processes resource changes automatically. To explicitly reprocess an existing `resource/photo.png`, run
|
||||||
|
the client in another terminal using the same workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme auto_resource include_images=true changes='[{"path":"resource/photo.png","change":"modified"}]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Images wider or taller than 2048px are downscaled,
|
||||||
|
and provider-unfriendly formats are re-encoded, in memory for the request only; the original file under
|
||||||
|
`resource/` is never modified. Before a full decode, image dimensions are checked against a default limit of 40,000,000
|
||||||
|
pixels; images over the limit and Pillow decompression-bomb warnings fail only that resource. EXIF orientation is
|
||||||
|
applied to the in-memory request copy before resizing or conversion. Oversized JPEGs first use decoder-level
|
||||||
|
downsampling, followed by a final thumbnail pass when needed. The VLM request MIME and the card's frontmatter
|
||||||
|
`media_type` use the format Pillow detects from the image bytes, rather than trusting the filename extension. When an
|
||||||
|
image changes, its card is updated in place; when the image is deleted, the card is removed with it, provided image
|
||||||
|
processing is enabled.
|
||||||
|
|
||||||
|
Image preprocessing uses Pillow from the `core` extra. HEIC resources additionally require the optional
|
||||||
|
`image-heif` extra: `pip install "reme-ai[image-heif]"`. Other supported image formats do not load or require the HEIF
|
||||||
|
plugin.
|
||||||
|
|
||||||
## Resource Cards
|
## Resource Cards
|
||||||
|
|
||||||
Each resource file produces one daily resource card. The system initially uses the resource file's stem as a temporary
|
Each resource file produces one daily resource card. The system initially uses the resource file's stem as a temporary
|
||||||
path. After the agent writes the card, the file is renamed according to its frontmatter `name`:
|
path. After the matching processor writes the card, the file is renamed according to its frontmatter `name`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
resource/2026-06-20/market-report.md
|
resource/2026-06-20/market-report.md
|
||||||
|
|
@ -69,9 +134,14 @@ The resource card links to the original file through frontmatter:
|
||||||
source_resource: "[[resource/2026-06-20/market-report.md]]"
|
source_resource: "[[resource/2026-06-20/market-report.md]]"
|
||||||
```
|
```
|
||||||
|
|
||||||
When a resource changes, Auto Resource finds and updates the corresponding card through `source_resource`. When a
|
When a resource changes, Auto Resource finds and updates the corresponding card through an exact `source_resource`
|
||||||
resource is deleted, its daily note is also removed. The older `daily/YYYY-MM-DD/<resource_stem>.md` naming convention
|
match. When an enabled resource is deleted, only the explicitly linked daily note is removed. A same-stem note without that
|
||||||
remains supported as a fallback.
|
provenance marker is treated as user-owned and left untouched; new resource cards use a collision-free path instead.
|
||||||
|
|
||||||
|
A failed call may still have changed a card; `modified` records whether the file changed. If the agent writes the card
|
||||||
|
and then fails or is cancelled, the written content stays on disk. ReMe tries to complete metadata and update the day's
|
||||||
|
index for the card linked through `source_resource`, while preserving the original error or cancellation. A failed
|
||||||
|
image-note format check also leaves the written content in place. Failed calls are not retried automatically.
|
||||||
|
|
||||||
## Daily Index
|
## Daily Index
|
||||||
|
|
||||||
|
|
@ -93,13 +163,12 @@ resource, open its corresponding resource card.
|
||||||
|
|
||||||
The interpreted daily note is optimized for readability; the original resource is retained for trust and verification.
|
The interpreted daily note is optimized for readability; the original resource is retained for trust and verification.
|
||||||
|
|
||||||
Auto Resource does not move the original file. It remains at its original path under `resource/`. Text resources can
|
Auto Resource does not move the original file. It remains at its original path under `resource/`. Resources can
|
||||||
therefore enter the daily memory flow while their source files stay in their original location.
|
therefore enter the daily memory flow while their source files stay in their original location.
|
||||||
|
|
||||||
## What Happens Next
|
## What Happens Next
|
||||||
|
|
||||||
Auto Resource only creates resource interpretations in the daily layer. To distill long-term knowledge from resources
|
Auto Resource only creates resource interpretations in the daily layer. To distill long-term knowledge from resources
|
||||||
into
|
into `digest/`, use [Auto Dream](./auto_dream.md). The default live index covers daily cards and digest nodes. Manual
|
||||||
`digest/`, use [Auto Dream](./auto_dream.md). The default live index covers daily cards and digest nodes. Run
|
`reindex` only rebuilds search indexes from chunks already accepted by an ingestion path; it does not add the original
|
||||||
`reme reindex`
|
resource files to search. See [Memory Search](./memory_search.md).
|
||||||
when original resource files must also be directly searchable; see [Memory Search](./memory_search.md).
|
|
||||||
|
|
|
||||||
172
docs/en/blog_20260920.md
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
# ReMe Memory Tags
|
||||||
|
|
||||||
|
Any memory system used over the long term eventually runs into a deceptively simple problem: **as memories accumulate, how do you search only the right subset?**
|
||||||
|
|
||||||
|
Suppose you and an agent have discussed three projects, all involving a launch, a budget, and an owner. Six months later, you ask:
|
||||||
|
|
||||||
|
> "What else do we need to confirm before launch?"
|
||||||
|
|
||||||
|
There is nothing wrong with the question, but it provides too few cues. Keyword search may retrieve every document that mentions "launch," while semantic search may blend experiences from several similar projects. Both find memories with similar content, but neither necessarily knows which project, company, or person you mean right now.
|
||||||
|
|
||||||
|
Human recall rarely works this way. We seldom run a full-text search across every experience at once. Instead, we begin with a few cues: **the ones about Alice, Project A, or that discussion from last year.** Once the scope narrows, the details begin to surface.
|
||||||
|
|
||||||
|
That is why ReMe adds memory tags. Each Markdown memory can express not only what it says, but also who or what it is mainly about—and that cue can participate directly in retrieval.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="../figure/reme-blog/reme-blog-memory-tags.svg" alt="ReMe builds an index from Markdown tags and filters the search scope" width="100%">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Why Memory Tags?
|
||||||
|
|
||||||
|
ReMe already uses BM25 for keyword search, optional embeddings for semantic similarity, and Wikilinks for traversing relationships between memories. Memory tags do not replace any of them. They add another dimension: **retrieval scope.**
|
||||||
|
|
||||||
|
Think of the three mechanisms as answering different questions:
|
||||||
|
|
||||||
|
- The query answers, "What am I looking for now?"
|
||||||
|
- A Wikilink answers, "Which memories are related to this one?"
|
||||||
|
- A memory tag answers, "Which memories should I search first?"
|
||||||
|
|
||||||
|
For example, "How did we handle the budget overrun?" may apply to many projects. If the search also includes `Project_A`, the agent can first narrow the scope to files related to Project A, then look for the specific details about the overrun.
|
||||||
|
|
||||||
|
Directories cannot fully solve this problem. A meeting note may concern Alice, Project A, and a customer at the same time, but a file normally occupies only one place on disk. Tags give the same memory multiple entry points without changing its original directory structure.
|
||||||
|
|
||||||
|
## Let Each Memory Say Who or What It Is About
|
||||||
|
|
||||||
|
ReMe memories remain plain Markdown. Tags live directly in YAML frontmatter, for example:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: Project A pre-launch checklist
|
||||||
|
description: Alice confirmed the launch window, rollback conditions, and customer notification order.
|
||||||
|
memory_tags:
|
||||||
|
- Alice
|
||||||
|
- Project_A
|
||||||
|
---
|
||||||
|
|
||||||
|
Project A is scheduled to launch on Thursday evening. Complete regression
|
||||||
|
testing first and have Alice confirm the customer notification. Roll back if
|
||||||
|
the error rate exceeds the agreed threshold.
|
||||||
|
```
|
||||||
|
|
||||||
|
The default field is named `memory_tags`. The name is intentional: this is not a loose collection of broad article keywords. It answers a more stable question:
|
||||||
|
|
||||||
|
> **Which real-world person or thing is this Markdown memory about?**
|
||||||
|
|
||||||
|
An entity can be a person, organization, company, project, or asset—for example, `Alice`, `CATL`, `Project_A`, or `Gold`. Compared with broad topics such as "work," "important," or "meeting," entities make better anchors for long-term memory because people, organizations, and projects tend to recur across many conversations.
|
||||||
|
|
||||||
|
In the default configuration, Auto Memory (`auto_memory`, `auto_memory_cc`) and Auto Dream (`auto_dream`, `dream_cron`) generate these tags for daily and digest Markdown files actually added or modified during the current run. Before tagging, the workflow reads the full document and its existing frontmatter, then checks tags already used in the workspace. It prefers an existing spelling for the same entity so that `Project_A`, `project a`, and `项目A` do not silently become three separate tags. Manual imports and edits do not trigger automatic tagging; existing `memory_tags` values are synchronized to the Tag Index by the file-watching workflow.
|
||||||
|
|
||||||
|
By default, a file receives only its most important entity. Multiple tags are used only when the document genuinely centers on multiple independent entities, and the total remains limited. A document without a clear core entity can use an empty list:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
memory_tags: []
|
||||||
|
```
|
||||||
|
|
||||||
|
This matters more than tagging for its own sake. More tags do not make a memory richer; too many broad tags only turn every filtered search back into a workspace-wide search.
|
||||||
|
|
||||||
|
Of course, `memory_tags` is only ReMe's default convention. The frontmatter field read by the tag index is configurable, and tag values remain under the user's control. Teams that already use `entities`, `people`, or another field can adapt the index to their files instead of migrating Markdown into a closed format.
|
||||||
|
|
||||||
|
## How Does the Tag Index Work?
|
||||||
|
|
||||||
|
After reading frontmatter, ReMe builds two simple cue maps: which files belong to a tag, and which tags belong to a file. For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Alice -> daily/project-a-launch.md
|
||||||
|
Project_A -> daily/project-a-launch.md
|
||||||
|
|
||||||
|
daily/project-a-launch.md -> Alice, Project_A
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a bidirectional index derived from Markdown files. Relationships update when memories are created or modified, and stale relationships disappear when files are deleted. Tag comparison is case-insensitive and normalizes details such as whitespace, reducing accidental splits caused by spelling variations.
|
||||||
|
|
||||||
|
The index does not replace files or become a new source of truth. The real tags remain in user-visible, editable frontmatter. If the index is lost, it can be rebuilt from the Markdown metadata in the current file graph:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme reindex scope=tag
|
||||||
|
```
|
||||||
|
|
||||||
|
This follows ReMe's usual principle: **files belong to the user, indexes serve the files, and indexes are always rebuildable.**
|
||||||
|
|
||||||
|
To inspect the tags in a workspace and see how many files each tag covers, list them directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme list_tags order_by=file_count order=desc
|
||||||
|
```
|
||||||
|
|
||||||
|
Besides supporting search, this makes the structure of the memory workspace observable. You can quickly see that a project has accumulated many memories, or notice that one person's name has been split across several near-duplicate spellings.
|
||||||
|
|
||||||
|
## How Do Tags Participate in Search?
|
||||||
|
|
||||||
|
The most important role of memory tags is not display, but filtering.
|
||||||
|
|
||||||
|
Consider the earlier example. A natural-language query by itself looks like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme search query="What else do we need to confirm before launch?"
|
||||||
|
```
|
||||||
|
|
||||||
|
That searches the entire searchable memory scope. Add a tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme search \
|
||||||
|
query="What else do we need to confirm before launch?" \
|
||||||
|
tags='["Project_A"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
ReMe first uses the Tag Index to find files tagged `Project_A`. BM25 and optional vector retrieval then produce direct matches only from those files, after which ranking fusion proceeds as usual.
|
||||||
|
|
||||||
|
There is one important boundary: tag filtering constrains direct retrieval hits, but it does not cut off Wikilink relationships. Default link expansion may still list the paths, names, and descriptions of neighboring memories outside the tag scope so the agent can decide whether to read further. Those neighbors do not become direct keyword or vector-search hits merely because they were listed.
|
||||||
|
|
||||||
|
The flow can be summarized as follows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Natural-language question + tag cue
|
||||||
|
↓
|
||||||
|
Tag Index identifies candidate files
|
||||||
|
↓
|
||||||
|
Keyword / semantic search within those files
|
||||||
|
↓
|
||||||
|
Return direct matching passages and optionally list relationships
|
||||||
|
(related neighbors may fall outside the tag scope)
|
||||||
|
```
|
||||||
|
|
||||||
|
Tag filtering can also be combined with date conditions—for example, to inspect memories created for a project during the last month. Each condition narrows a separate dimension: the entity specifies who or what, the date specifies when, and the query specifies what you want to know.
|
||||||
|
|
||||||
|
When several tags are supplied, ReMe currently keeps files that match any of them. For example, `tags=[Alice, Project_A]` retrieves memories about Alice or Project A, then lets the query determine which results rank first. This lets an agent widen the candidate set with several plausible entity cues without returning to a workspace-wide search.
|
||||||
|
|
||||||
|
## What Changes in Practice?
|
||||||
|
|
||||||
|
Memory tags do not make a tag mandatory for every search. Searches without tags continue to work as before. The real change is that when a user or agent already knows part of the context, that context no longer has to remain hidden inside a vague query.
|
||||||
|
|
||||||
|
### 1. The Same Question Is Less Likely to Drift into Another Project
|
||||||
|
|
||||||
|
"Why was it delayed last time?", "Who approved the budget?", and "What remains before launch?" all depend heavily on context. Tags establish the project or person first, reducing the chance that memories with similar names or content enter the candidate set.
|
||||||
|
|
||||||
|
### 2. Memories About the Same Entity Can Accumulate Across Time
|
||||||
|
|
||||||
|
Alice may appear in meeting notes, project decisions, personal preferences, and retrospectives. Those files do not need to move into one directory. A shared tag creates an entity view across directories and dates.
|
||||||
|
|
||||||
|
### 3. Memory Structure Is Visible to Both People and Agents
|
||||||
|
|
||||||
|
Tags are not internal fields hidden in a specialized database. Users can open, edit, and review them in Markdown. An agent can inspect the tags that exist before deciding which entity cue to include in a search. Incorrect tags can be found, and naming can converge over time.
|
||||||
|
|
||||||
|
### 4. Search Becomes Easier to Explain
|
||||||
|
|
||||||
|
When a result is unexpected, the pipeline can be inspected step by step: does the document contain the right `memory_tags`, does the Tag Index include the path, or did keyword and semantic ranking fail to match it? This chain is easier to diagnose and correct than one opaque relevance score.
|
||||||
|
|
||||||
|
## Tags Are Retrieval Cues, Not a Taxonomy
|
||||||
|
|
||||||
|
The goal is not to turn a personal knowledge base into a carefully maintained classification tree. Real memories naturally overlap: one conversation may involve both a person and a project, while one decision may belong to today's meeting and shape a retrospective months later.
|
||||||
|
|
||||||
|
Memory tags are closer to the retrieval cues used by human memory. Seeing a person's name reminds us of shared experiences; thinking about a project brings related decisions, problems, and commitments to mind. A cue is not the memory itself, but it helps us enter the right context faster.
|
||||||
|
|
||||||
|
What ReMe does is deliberately simple:
|
||||||
|
|
||||||
|
- Preserve complete, readable memories in Markdown.
|
||||||
|
- Use `memory_tags` to express who or what a memory is about.
|
||||||
|
- Connect entities and files through a rebuildable Tag Index.
|
||||||
|
- Narrow the scope by tag before using keywords, semantics, and links to find the answer.
|
||||||
|
|
||||||
|
In this way, memory becomes more than a collection of full-text-searchable documents. It begins to acquire a structure that better matches how people associate ideas.
|
||||||
|
|
||||||
|
When you say, "That Alice project from last time," the agent receives more than a sentence. It receives a cue it can actually follow back into the past.
|
||||||
178
docs/en/configuration.md
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
---
|
||||||
|
title: Configuration
|
||||||
|
description: ReMe configuration files, environment expansion, command-line overrides, and core components.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
ReMe uses YAML or JSON to describe its Service, Jobs, and Components. The built-in default is `reme/config/default.yaml`. Select another configuration at startup and apply command-line overrides when needed.
|
||||||
|
|
||||||
|
## Precedence
|
||||||
|
|
||||||
|
Configuration is merged in this order, with later values winning:
|
||||||
|
|
||||||
|
1. `application_defaults` from enabled plugins.
|
||||||
|
2. The selected file; `default` is used when none is specified.
|
||||||
|
3. CLI dot-notation overrides.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start
|
||||||
|
reme start config=demo
|
||||||
|
reme start config=cookbook
|
||||||
|
reme start config=/absolute/path/to/app.yaml
|
||||||
|
reme start service.port=8181 workspace_dir=/data/reme
|
||||||
|
```
|
||||||
|
|
||||||
|
`config` accepts a built-in name or a `.yaml`, `.yml`, or `.json` file. Overrides are deep-merged, so changing `service.port` preserves sibling service settings.
|
||||||
|
The optional `cookbook` variant extends `default` and composes the separately installed Auto Fin, Daily Paper, and
|
||||||
|
DingTalk plugins. It requires the three DingTalk application credential environment variables before configuration
|
||||||
|
loading. It also enables `text-embedding-v4` vector retrieval, uses AgentScope with
|
||||||
|
`${LLM_MODEL_NAME:-qwen3.8-max}` by default, and runs the DingTalk bridge through Claude Code with the same
|
||||||
|
`LLM_MODEL_NAME` and `LLM_API_KEY`.
|
||||||
|
|
||||||
|
## CLI values
|
||||||
|
|
||||||
|
Arguments use `key=value`; leading `-` or `--` is accepted:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start --service.port=8181 --service.web_enabled=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Values support null, booleans, numbers, JSON arrays and objects, quoted JSON strings, and plain strings. Numeric-looking values with leading zeroes, such as `007`, remain strings. Quote values such as `"true"` in JSON when they must remain strings.
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
Configuration recursively expands:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
api_key: ${LLM_API_KEY}
|
||||||
|
base_url: ${LLM_BASE_URL:-https://example.com/v1}
|
||||||
|
```
|
||||||
|
|
||||||
|
`${VAR}` fails when undefined; `${VAR:-default}` uses its fallback. ReMe also searches for `.env` from the command's working directory through at most five parents.
|
||||||
|
|
||||||
|
Keep secrets in `.env` or the process environment, never in committed configuration.
|
||||||
|
|
||||||
|
## Application fields
|
||||||
|
|
||||||
|
| Field | Default | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `app_name` | `ReMe` | Display name |
|
||||||
|
| `workspace_dir` | `.reme` | User-owned workspace root, normalized to an absolute path |
|
||||||
|
| `metadata_dir` | `metadata` | Rebuildable indexes, graphs, and catalogs |
|
||||||
|
| `session_dir` | `session` | Agent sessions; standard transcripts use `session/dialog` |
|
||||||
|
| `mem_session_dir` | `mem_session` | Agent-wrapper sessions and configuration |
|
||||||
|
| `resource_dir` | `resource` | External resources |
|
||||||
|
| `daily_dir` | `daily` | Daily memory |
|
||||||
|
| `digest_dir` | `digest` | Consolidated long-term memory |
|
||||||
|
| `timezone` | `Asia/Shanghai` | IANA timezone used for dates and cron jobs |
|
||||||
|
| `language` | empty | Default language for LLM interactions |
|
||||||
|
| `plugins` | `[]` | Installed plugins enabled for this Application |
|
||||||
|
| `service` | HTTP | Service configuration |
|
||||||
|
| `jobs` | default Jobs | Job configurations by name |
|
||||||
|
| `components` | defaults | Components grouped by type and name |
|
||||||
|
|
||||||
|
`session_dir` must remain workspace-relative.
|
||||||
|
|
||||||
|
## Enabling jobs
|
||||||
|
|
||||||
|
`jobs.<name>.enabled` defaults to `true` for all Job types. Disabled jobs retain their configuration but do not
|
||||||
|
start, expose service interfaces, or accept calls through `Application.run_job()` / `run_stream_job()`.
|
||||||
|
For example, disable ReMe's Dream cron when a host plugin owns the schedule:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start jobs.dream_cron.enabled=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the service to apply the override. The separate `auto_dream` API remains available, and other jobs continue running.
|
||||||
|
`enable_serve` independently controls service exposure: `enabled=true, enable_serve=false` keeps a Job available for
|
||||||
|
local calls. Background and cron jobs are never service-exposed.
|
||||||
|
|
||||||
|
## LLM
|
||||||
|
|
||||||
|
The default LLM uses an OpenAI-compatible interface:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
components:
|
||||||
|
as_llm:
|
||||||
|
default:
|
||||||
|
backend: openai
|
||||||
|
model: qwen3.7-plus
|
||||||
|
context_size: 200000
|
||||||
|
credential:
|
||||||
|
api_key: ${LLM_API_KEY:-}
|
||||||
|
base_url: ${LLM_BASE_URL:-}
|
||||||
|
```
|
||||||
|
|
||||||
|
Built-in registrations include `openai`, `anthropic`, `dashscope`, `deepseek`, `gemini`, `moonshot`, `ollama`, and `xai`. Their detailed model fields follow the corresponding AgentScope wrappers.
|
||||||
|
|
||||||
|
File operations, BM25 search, wikilink traversal, and `proactive_read` do not require an LLM. Evolution workflows such
|
||||||
|
as `auto_memory`, `auto_resource`, `auto_dream`, and proactive refresh do.
|
||||||
|
|
||||||
|
## Embeddings
|
||||||
|
|
||||||
|
Vector retrieval is disabled by default. Credentials alone do not enable it: configure `as_embedding`, `embedding_store`, and connect the store to `file_store`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
components:
|
||||||
|
as_embedding:
|
||||||
|
default:
|
||||||
|
backend: openai
|
||||||
|
model: text-embedding-v4
|
||||||
|
dimensions: 1024
|
||||||
|
credential:
|
||||||
|
api_key: ${EMBEDDING_API_KEY}
|
||||||
|
base_url: ${EMBEDDING_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
|
||||||
|
embedding_store:
|
||||||
|
default:
|
||||||
|
backend: local
|
||||||
|
as_embedding: default
|
||||||
|
file_store:
|
||||||
|
default:
|
||||||
|
backend: local
|
||||||
|
embedding_store: default
|
||||||
|
keyword_index: default
|
||||||
|
file_graph: default
|
||||||
|
```
|
||||||
|
|
||||||
|
Rebuild the embedding index after changing the model or dimensions.
|
||||||
|
|
||||||
|
## Service and Jobs
|
||||||
|
|
||||||
|
Minimal HTTP configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service:
|
||||||
|
backend: http
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 2333
|
||||||
|
web_enabled: true
|
||||||
|
mcp_enabled: true
|
||||||
|
mcp_path: /mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
A Job declares a backend, parameter schema, and ordered Steps:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
example:
|
||||||
|
backend: base
|
||||||
|
description: Example job
|
||||||
|
parameters:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
text: { type: string }
|
||||||
|
required: [text]
|
||||||
|
steps:
|
||||||
|
- backend: example_step
|
||||||
|
```
|
||||||
|
|
||||||
|
Set `enable_serve: false` to keep a Job internal. Background and cron Jobs are never service-exposed.
|
||||||
|
|
||||||
|
## Inspect the effective configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme app_config
|
||||||
|
```
|
||||||
|
|
||||||
|
The result is the merged, validated configuration with secrets redacted. Use it when diagnosing plugin or override precedence. The authoritative contracts remain `reme/schema/application_config.py` and `reme/config/default.yaml`.
|
||||||
|
|
@ -39,8 +39,8 @@ The project requires Python 3.11 or later. A virtual environment is recommended:
|
||||||
```bash
|
```bash
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
pip install -e packages/reme_ai_studio -e ".[dev,full]"
|
pip install -e reme_studio -e ".[dev,full]"
|
||||||
cd website
|
cd reme_studio
|
||||||
npm ci
|
npm ci
|
||||||
npm run build:static
|
npm run build:static
|
||||||
cd ..
|
cd ..
|
||||||
|
|
@ -209,6 +209,13 @@ Documentation lives under:
|
||||||
docs/
|
docs/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
User guides should have matching `docs/zh/` and `docs/en/` versions and appear in the corresponding navigation in
|
||||||
|
`docs/.vitepress/config.mts`. The ReMe Studio, TypeScript, plugin, and benchmark READMEs remain canonical in their own
|
||||||
|
directories; `github-pages/scripts/generate-content.mjs` mirrors them during builds. Never edit `.generated/` or `dist/`.
|
||||||
|
|
||||||
|
The Job API reference is generated from `reme/config/default.yaml`. Update that YAML and its tests when a default Job
|
||||||
|
contract changes rather than editing generated pages.
|
||||||
|
|
||||||
Documentation should:
|
Documentation should:
|
||||||
|
|
||||||
- Use clear titles that directly identify a capability or flow.
|
- Use clear titles that directly identify a capability or flow.
|
||||||
|
|
@ -216,6 +223,15 @@ Documentation should:
|
||||||
- Use real repository paths such as `reme/config/default.yaml`, `reme/steps/`, and `tests/unit/`.
|
- Use real repository paths such as `reme/config/default.yaml`, `reme/steps/`, and `tests/unit/`.
|
||||||
- Describe default behavior according to the current code, `pyproject.toml`, and default configuration.
|
- Describe default behavior according to the current code, `pyproject.toml`, and default configuration.
|
||||||
|
|
||||||
|
Validate the documentation site with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd github-pages
|
||||||
|
npm ci
|
||||||
|
npm test
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
|
||||||
156
docs/en/docker.md
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
---
|
||||||
|
title: Docker Deployment
|
||||||
|
description: Run ReMe and Studio in Docker with a user-owned persistent workspace.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Docker Deployment
|
||||||
|
|
||||||
|
The image includes ReMe's `core` and HEIF image dependencies and the Studio static frontend. One HTTP process serves the API,
|
||||||
|
Studio at `/`, and MCP at `/mcp`. The default configuration keeps embeddings disabled; file operations and BM25 search do
|
||||||
|
not require model credentials.
|
||||||
|
|
||||||
|
## Build and run with Compose
|
||||||
|
|
||||||
|
Use Docker Engine or Docker Desktop with Compose **2.24.0 or newer**. From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .reme
|
||||||
|
docker compose up --build -d
|
||||||
|
docker compose logs -f reme
|
||||||
|
```
|
||||||
|
|
||||||
|
Open <http://127.0.0.1:2333>. Compose binds the host port to loopback and mounts `./.reme` at `/data`. The source checkout and
|
||||||
|
Studio assets are not mounted over the installed application.
|
||||||
|
|
||||||
|
On Linux, if your workspace is not owned by UID/GID 1000, set the process identity before starting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export REME_UID=$(id -u)
|
||||||
|
export REME_GID=$(id -g)
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
For model-powered memory evolution, copy `deploy/docker/example.env` to `.env` if you do not already have one, then fill in
|
||||||
|
your model credentials. Compose injects this optional file at runtime; Docker builds exclude `.env` files. Compose's
|
||||||
|
`--env-file` controls variable interpolation; `REME_ENV_FILE` selects the file injected into the container.
|
||||||
|
|
||||||
|
## Use a published image
|
||||||
|
|
||||||
|
The Docker workflow publishes `ghcr.io/agentscope-ai/reme:main` after successful main-branch checks. Stable GitHub releases
|
||||||
|
publish their package version and `latest`; prereleases do not update `latest`. Both Linux amd64 and arm64 images are tested
|
||||||
|
before their combined tags are published. Publication starts when the workflow is enabled in the repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p "$HOME/.reme"
|
||||||
|
docker run -d --name reme \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
-p 127.0.0.1:2333:2333 \
|
||||||
|
--mount "type=bind,source=$HOME/.reme,target=/data" \
|
||||||
|
--restart unless-stopped \
|
||||||
|
ghcr.io/agentscope-ai/reme:main
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `--env-file /path/to/model.env` before the image name when using model credentials. For reproducible deployments,
|
||||||
|
replace `main` with a released version or image digest. To use the image with Compose, set `REME_IMAGE`, then run
|
||||||
|
`docker compose pull` and `docker compose up -d --no-build`.
|
||||||
|
|
||||||
|
## Paths, configuration, and ports
|
||||||
|
|
||||||
|
The image runs as UID/GID 1000 by default. `/data` contains the entire workspace: source sessions, resources, daily notes,
|
||||||
|
digest notes, and rebuildable metadata. Create the host directory yourself and make it writable by the configured user.
|
||||||
|
Mounting only `metadata/` does not preserve the source memories. Paths in a custom configuration refer to the container's
|
||||||
|
filesystem; additional paths need additional mounts.
|
||||||
|
|
||||||
|
| Setting | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `REME_WORKSPACE_DIR` | Container workspace; image default `/data`, fixed to `/data` by Compose |
|
||||||
|
| `REME_CONFIG` | Existing config name or mounted YAML/JSON path; unset uses the built-in default |
|
||||||
|
| `REME_HOST` | HTTP bind address; image and Compose use `0.0.0.0` |
|
||||||
|
| `REME_PORT` | Container port override; Compose defaults to `2333` |
|
||||||
|
| `REME_TIMEZONE` | Optional application timezone override; otherwise the application default applies |
|
||||||
|
| `REME_DATA_DIR` | Compose host workspace directory; default `./.reme` |
|
||||||
|
| `REME_PUBLISHED_PORT` | Compose host port; default `2333`, independent of the container port |
|
||||||
|
| `REME_BIND_ADDRESS` | Compose host bind address; default `127.0.0.1` |
|
||||||
|
| `REME_UID`, `REME_GID` | Compose process identity; both default to `1000` |
|
||||||
|
| `REME_ENV_FILE` | Optional Compose runtime environment file; default `.env` |
|
||||||
|
|
||||||
|
Explicit `start key=value` arguments override container environment settings, which override the loaded configuration for
|
||||||
|
those keys. Other keys retain ReMe's normal deep merge behavior. File logging defaults to off in the image; use container
|
||||||
|
logs. `log_to_file=true` explicitly enables file logs under `/app/logs`, which requires a separate mount to persist them.
|
||||||
|
The temporary home directory `/tmp/reme-home` and probe address file are disposable, not workspace storage.
|
||||||
|
|
||||||
|
To customize the full job/component configuration, copy `reme/config/default.yaml` to `reme.yaml`, edit it, set
|
||||||
|
`REME_CONFIG=/etc/reme/config.yaml` in `.env`, and add `compose.override.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
reme:
|
||||||
|
volumes:
|
||||||
|
- ./reme.yaml:/etc/reme/config.yaml:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the `health_check` Job enabled and in `service.jobs` if you use an allowlist. The image's probe uses HTTP; when
|
||||||
|
overriding the service to CLI or MCP stdio, disable the Docker health check with `--no-healthcheck` (or Compose
|
||||||
|
`healthcheck: {disable: true}`).
|
||||||
|
|
||||||
|
To override startup without changing the image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -p 127.0.0.1:2444:2444 \
|
||||||
|
--mount "type=bind,source=$HOME/.reme,target=/data" \
|
||||||
|
reme:local start service.port=2444 timezone=UTC
|
||||||
|
docker compose exec reme reme health_check
|
||||||
|
docker compose exec reme reme status
|
||||||
|
```
|
||||||
|
|
||||||
|
Other commands pass through unchanged, including `reme start job=version` for a one-shot job or `python` for diagnostics.
|
||||||
|
From a host CLI, supply the published address explicitly, for example `reme health_check host=127.0.0.1 port=2444`.
|
||||||
|
Host process discovery cannot reconstruct a container's startup arguments. Configure agent integrations to use the
|
||||||
|
published HTTP or MCP endpoint instead of starting a second native ReMe on the same workspace.
|
||||||
|
|
||||||
|
## Networking and optional tools
|
||||||
|
|
||||||
|
`127.0.0.1` inside a container refers to that container. A model server on the host needs a reachable host address, such as
|
||||||
|
`host.docker.internal` on Docker Desktop. On Linux, add `extra_hosts: ["host.docker.internal:host-gateway"]` to the service
|
||||||
|
and configure the model URL accordingly. Another Compose service is reachable by its service name.
|
||||||
|
|
||||||
|
The HTTP action API has no built-in authentication and includes write/delete operations. Keep the default loopback port
|
||||||
|
publication. For access from another machine, place an authenticated TLS proxy in front of the service and restrict direct
|
||||||
|
access to its port; the same restriction must cover Studio, HTTP Jobs, and MCP.
|
||||||
|
|
||||||
|
The image includes the configured agent SDK dependencies, but host OAuth files, transcripts, plugins, external MCP
|
||||||
|
executables, and host workspace paths are not automatically available. Mount required data explicitly and install extra
|
||||||
|
plugins/tools in a derived image so that recreating the container preserves the installation. Keep credentials out of
|
||||||
|
Docker build arguments and layers. Optional FAISS/zvec backends also depend on the capabilities of the target machine.
|
||||||
|
|
||||||
|
## Health, upgrade, and recovery
|
||||||
|
|
||||||
|
Docker posts to the existing `/health_check` Job and requires both `success=true` and `metadata.health.healthy=true`.
|
||||||
|
The probe address follows effective configuration and CLI port overrides, and bypasses outbound proxy settings.
|
||||||
|
Initialization has a 120-second health grace period; larger workspaces may need a longer Compose `healthcheck.start_period`.
|
||||||
|
This reports component health, not whether a remote model will accept a future request. An unhealthy Docker status alone
|
||||||
|
does not trigger `restart: unless-stopped`; that policy restarts exited processes.
|
||||||
|
|
||||||
|
Before upgrading, stop writes and back up the **complete** host workspace and your deployment configuration. Then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose stop
|
||||||
|
# Back up the configured host workspace here.
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d --no-build
|
||||||
|
docker compose exec reme reme health_check
|
||||||
|
```
|
||||||
|
|
||||||
|
For a locally built deployment, replace `pull` and `up --no-build` with `docker compose up --build -d`. Compose allows
|
||||||
|
60 seconds for orderly shutdown. Recreating containers leaves the bind-mounted workspace intact; use one ReMe writer
|
||||||
|
process per workspace. See [backup and recovery](./operations.md) for restoring derived state without deleting memory.
|
||||||
|
|
||||||
|
For container validation after a local build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t reme:local .
|
||||||
|
python scripts/test_docker_image.py --image reme:local
|
||||||
|
```
|
||||||
|
|
||||||
|
The smoke check uses a disposable workspace and no model credentials. It verifies Studio, HTTP and MCP, file containment,
|
||||||
|
non-root execution, graceful shutdown, and memory search after replacing a container on a different port.
|
||||||
72
docs/en/faq.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
title: Frequently Asked Questions
|
||||||
|
description: Quick answers for ReMe installation, services, models, retrieval, files, and plugins.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## Do basic file operations require a model API key?
|
||||||
|
|
||||||
|
No. `write`, `read`, `list`, `stat`, BM25 search, wikilink traversal, and `proactive_read` work without model
|
||||||
|
credentials. `auto_memory`, `auto_resource`, `auto_dream`, and proactive refresh require an LLM.
|
||||||
|
|
||||||
|
## Why is search still BM25-only after setting an embedding key?
|
||||||
|
|
||||||
|
Embeddings are disabled by default. Configure `as_embedding` and `embedding_store`, then connect `file_store.default.embedding_store` to that component. See [Configuration](./configuration.md#embeddings).
|
||||||
|
|
||||||
|
## Why did `reme reindex` not discover a new file?
|
||||||
|
|
||||||
|
`reindex` rebuilds indexes from current `file_chunks`; it does not scan the workspace. Check `index_update_loop`, the watched directory and extension, and `health_check`.
|
||||||
|
|
||||||
|
## How do I use another workspace?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start workspace_dir=/absolute/path/to/memory
|
||||||
|
```
|
||||||
|
|
||||||
|
Ordinary CLI calls discover the running service, so they do not need the workspace argument again.
|
||||||
|
|
||||||
|
## What if port 2333 is occupied?
|
||||||
|
|
||||||
|
Do not stop an unknown listener. Select another port:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start service.port=8181
|
||||||
|
```
|
||||||
|
|
||||||
|
Then confirm it with `reme find_reme`.
|
||||||
|
|
||||||
|
## Why is an installed plugin missing its Jobs?
|
||||||
|
|
||||||
|
Installation only makes the distribution discoverable in the active Python environment. Enable it for the Application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start plugins='["auto-fin"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart a running service after changing package or enablement state.
|
||||||
|
|
||||||
|
## May I edit workspace Markdown directly?
|
||||||
|
|
||||||
|
Yes. Files are the source of truth and watchers ingest changes. Keep frontmatter valid, use complete workspace-relative wikilinks, and avoid unconditional concurrent saves.
|
||||||
|
|
||||||
|
## May I expose ReMe publicly?
|
||||||
|
|
||||||
|
Not with the default configuration alone. Jobs can write and delete, HTTP CORS is permissive, and there is no general authentication layer. Use a controlled network or authenticated TLS reverse proxy and restrict `service.jobs`.
|
||||||
|
|
||||||
|
## How should I back up and migrate memory?
|
||||||
|
|
||||||
|
Stop writes and back up the complete workspace. `session/`, `resource/`, `daily/`, and `digest/` are the key sources; `metadata/` can be backed up or rebuilt. See [Diagnostics, Backup, and Recovery](./operations.md).
|
||||||
|
|
||||||
|
## Why is Studio unavailable?
|
||||||
|
|
||||||
|
The base `reme-ai` package has no frontend assets. Install `reme-ai[web]` or `reme-ai[core]`, or set `service.web_static_dir`. Missing Studio assets do not disable the Job API.
|
||||||
|
|
||||||
|
## Which capabilities does the running service expose?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme help
|
||||||
|
reme app_config
|
||||||
|
```
|
||||||
|
|
||||||
|
Static documentation describes defaults; plugins and custom configuration may change the active service.
|
||||||
|
|
@ -55,11 +55,13 @@ Core layers:
|
||||||
reme/
|
reme/
|
||||||
reme.py # CLI entry point
|
reme.py # CLI entry point
|
||||||
application.py # Application assembly and lifecycle
|
application.py # Application assembly and lifecycle
|
||||||
|
plugin.py # installed plugin contract and entry-point loader
|
||||||
config/
|
config/
|
||||||
default.yaml # default service / jobs / components
|
default.yaml # default service / jobs / components
|
||||||
|
cookbook.yaml # Auto Fin + Daily Paper + DingTalk composition
|
||||||
config_parser.py # config=, dot notation, and env placeholder parsing
|
config_parser.py # config=, dot notation, and env placeholder parsing
|
||||||
components/
|
components/
|
||||||
component_registry.py # global registry R
|
component_registry.py # backend registry and application-local copies
|
||||||
base_component.py # ComponentMixin / BaseComponent / bind dependency declarations
|
base_component.py # ComponentMixin / BaseComponent / bind dependency declarations
|
||||||
runtime_context.py # context for one Job execution
|
runtime_context.py # context for one Job execution
|
||||||
job/ # BaseJob / StreamJob / BackgroundJob / CronJob
|
job/ # BaseJob / StreamJob / BackgroundJob / CronJob
|
||||||
|
|
@ -75,12 +77,17 @@ reme/
|
||||||
steps/
|
steps/
|
||||||
base_step.py # BaseStep, Ref, dispatch_steps
|
base_step.py # BaseStep, Ref, dispatch_steps
|
||||||
common/ # version, help, health_check, status, chat
|
common/ # version, help, health_check, status, chat
|
||||||
benchmark/ # LongMemEval / BEAM evaluation steps
|
|
||||||
cookbook/ # optional research workflow steps
|
|
||||||
file_io/ # read/write/edit/delete/move/frontmatter/daily
|
file_io/ # read/write/edit/delete/move/frontmatter/daily
|
||||||
index/ # watch/init/update/search/traverse
|
index/ # watch/init/update/search/traverse
|
||||||
evolve/ # auto_memory, auto_resource, auto_dream, proactive
|
evolve/ # auto_memory, auto_resource, auto_dream, proactive
|
||||||
transfer/ # upload/download
|
transfer/ # upload/download
|
||||||
|
plugins/
|
||||||
|
dingtalk/ # independent DingTalk integration plugin distribution
|
||||||
|
auto-fin/ # independent example plugin distribution
|
||||||
|
daily_paper/ # independent paper-research plugin distribution
|
||||||
|
integrations/
|
||||||
|
claude_code/ # Claude Code adapter and marketplace
|
||||||
|
hermes_agent/ # Hermes Agent memory-provider adapter
|
||||||
```
|
```
|
||||||
|
|
||||||
The default workspace directories are defined by `ApplicationConfig`:
|
The default workspace directories are defined by `ApplicationConfig`:
|
||||||
|
|
@ -167,8 +174,8 @@ HTTP service behavior:
|
||||||
|
|
||||||
After registering Job endpoints, the HTTP service can also mount the ReMe Studio single-page application. The default is
|
After registering Job endpoints, the HTTP service can also mount the ReMe Studio single-page application. The default is
|
||||||
`service.web_enabled=true`. Builds are resolved from `service.web_static_dir`, `REME_WEB_STATIC_DIR`, the optional
|
`service.web_enabled=true`. Builds are resolved from `service.web_static_dir`, `REME_WEB_STATIC_DIR`, the optional
|
||||||
`reme-ai-studio` package installed by the `web` and `core` extras, and source-tree locations such as
|
`reme_studio` package installed by the `web` and `core` extras, and source-tree locations such as
|
||||||
`website/dist-static`. If no `index.html` is found, only the frontend is skipped and the Job API remains available. The
|
`reme_studio/dist-static`. If no `index.html` is found, only the frontend is skipped and the Job API remains available. The
|
||||||
Studio `GET` fallback does not replace existing `POST /<job.name>` routes.
|
Studio `GET` fallback does not replace existing `POST /<job.name>` routes.
|
||||||
|
|
||||||
MCP service behavior:
|
MCP service behavior:
|
||||||
|
|
@ -216,14 +223,46 @@ The registry key is:
|
||||||
The same backend name can therefore exist under different component types. For example, `http` can be both a service
|
The same backend name can therefore exist under different component types. For example, `http` can be both a service
|
||||||
backend and a client backend.
|
backend and a client backend.
|
||||||
|
|
||||||
### 4.2 Registration Through Module Imports
|
`ComponentEnum` provides the built-in identifiers, but installed plugins may declare a new type with a namespaced
|
||||||
|
string such as `example.reranker`. Custom identifiers use lowercase letters and numbers separated by `.`, `_`, or `-`.
|
||||||
|
They are configured under `components` and participate in the same dependency ordering and lifecycle as built-ins.
|
||||||
|
|
||||||
Registration happens when a module is imported. `reme/components/__init__.py` imports component packages, while
|
### 4.2 Built-in and Plugin Registration
|
||||||
`reme/steps/__init__.py` imports `benchmark/common/cookbook/evolve/file_io/index/transfer`. Each package's `__init__.py`
|
|
||||||
then imports its concrete modules, causing `@R.register(...)` to execute.
|
|
||||||
|
|
||||||
After adding a Step file, make sure the package's `__init__.py` imports it. Otherwise, the backend will not appear in
|
Built-in implementations populate the built-in registry through package imports. ReMe freezes that template after
|
||||||
the registry.
|
bootstrap, and each `Application` receives a mutable copy. Runtime code resolves backends through the application's
|
||||||
|
registry rather than changing the process-wide template. ReMe then loads only the installed plugins explicitly named by
|
||||||
|
`plugins` in the resolved configuration. A plugin exposes its package through the `reme.plugins` Python entry-point
|
||||||
|
group. The package's `plugin.yaml` has two optional mappings: `backends` maps registration names to
|
||||||
|
`module:Class` targets, and `application_defaults` contributes a low-priority `ApplicationConfig` fragment. The
|
||||||
|
entry-point name is the plugin's identity.
|
||||||
|
Plugins are enabled explicitly through the application config's `plugins` list or a `plugins=[...]` CLI override.
|
||||||
|
Plugin registration therefore stays local to one application;
|
||||||
|
duplicate `(component_type, backend)` providers fail during assembly instead of overwriting each other.
|
||||||
|
|
||||||
|
The legacy Python `Plugin` descriptor and `reme.configs` entry points remain accepted during migration. Configuration
|
||||||
|
files can use `extends` to inherit another built-in, legacy plugin, or file-based configuration. See the independently
|
||||||
|
packaged [DingTalk](../../plugins/dingtalk/README.md), [Auto Fin](../../plugins/auto-fin/README.md), and
|
||||||
|
[Daily Paper](../../plugins/daily_paper/README.md) plugins.
|
||||||
|
|
||||||
|
Plugin packages are managed locally and remain separate from per-application activation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list
|
||||||
|
reme plugins install plugins/dingtalk
|
||||||
|
reme plugins install plugins/auto-fin
|
||||||
|
reme plugins install plugins/daily_paper
|
||||||
|
reme plugins show daily-paper
|
||||||
|
reme plugins validate daily-paper
|
||||||
|
reme plugins uninstall daily-paper
|
||||||
|
|
||||||
|
reme start config=cookbook
|
||||||
|
```
|
||||||
|
|
||||||
|
These management commands use the current Python interpreter's pip and never run through an HTTP or MCP service.
|
||||||
|
The built-in `cookbook` configuration composes the three plugins, adds DingTalk delivery to the two report pipelines,
|
||||||
|
and starts the DingTalk Agent bridge as a background Job. Enabling Auto Fin or Daily Paper alone keeps it independent
|
||||||
|
from DingTalk.
|
||||||
|
|
||||||
### 4.3 Component.bind
|
### 4.3 Component.bind
|
||||||
|
|
||||||
|
|
@ -387,7 +426,6 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- backend: dream_extract_step
|
- backend: dream_extract_step
|
||||||
- backend: dream_integrate_step
|
- backend: dream_integrate_step
|
||||||
- backend: dream_topics_step
|
|
||||||
- backend: dream_finish_step
|
- backend: dream_finish_step
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -399,9 +437,9 @@ The current implementation uses `croniter` to calculate the next trigger time. T
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
Jobs["default.yaml jobs"] --> BG["background<br/>index_update_loop<br/>resource_watch_loop<br/>digest_watch_loop"]
|
Jobs["default.yaml jobs"] --> BG["background<br/>index_update_loop<br/>resource_watch_loop<br/>digest_watch_loop"]
|
||||||
Jobs --> Cron["cron<br/>dream_cron<br/>optimize_index_cron"]
|
Jobs --> Cron["cron<br/>dream_cron<br/>proactive_refresh_cron<br/>optimize_index_cron"]
|
||||||
Jobs --> Stream["stream<br/>chat"]
|
Jobs --> Stream["stream<br/>chat"]
|
||||||
Jobs --> Base["base<br/>version / help / health_check / status / app_config<br/>search / node_search / traverse / graph_snapshot / reindex<br/>read / load / read_image / write / save / edit / delete / move / list / stat / frontmatter_*<br/>daily_list / daily_reindex / daily_write<br/>auto_memory / auto_memory_cc / auto_resource / auto_dream / proactive"]
|
Jobs --> Base["base<br/>version / help / health_check / status / app_config<br/>search / node_search / traverse / graph_snapshot / reindex<br/>read / load / read_image / write / save / edit / delete / move / list / stat / frontmatter_*<br/>daily_list / daily_reindex / daily_write<br/>auto_memory / auto_memory_cc / auto_resource / auto_dream / proactive_refresh / proactive_read"]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 7. Step Model
|
## 7. Step Model
|
||||||
|
|
@ -769,7 +807,6 @@ jobs:
|
||||||
- backend: dream_extract_step
|
- backend: dream_extract_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
- backend: dream_integrate_step
|
- backend: dream_integrate_step
|
||||||
- backend: dream_topics_step
|
|
||||||
- backend: dream_finish_step
|
- backend: dream_finish_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
```
|
```
|
||||||
|
|
|
||||||
9
docs/en/index.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
layout: home
|
||||||
|
markdownStyles: false
|
||||||
|
title: ReMe
|
||||||
|
titleTemplate: false
|
||||||
|
description: ReMe is a local-first, file-native memory system for agents.
|
||||||
|
---
|
||||||
|
|
||||||
|
<HomePage lang="en" />
|
||||||
100
docs/en/integrations.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
---
|
||||||
|
title: Agent Integrations
|
||||||
|
description: Connect ReMe to agents through the CLI, HTTP, MCP, Skills, and host adapters.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Integrations
|
||||||
|
|
||||||
|
ReMe keeps memory in an independent service and a user-owned workspace. Multiple agents can call the same memory system without binding storage to one model or host.
|
||||||
|
|
||||||
|
## Choose an interface
|
||||||
|
|
||||||
|
| Scenario | Recommended interface |
|
||||||
|
|---|---|
|
||||||
|
| Local script or hook | ReMe CLI |
|
||||||
|
| Application backend | HTTP Client |
|
||||||
|
| Tool-protocol host | MCP |
|
||||||
|
| DeepSeek Harness | [`@agentscope-ai/reme-dsh-plugin`](./integrations/dsh.md) profile bundle |
|
||||||
|
| OpenClaw | [`@agentscope-ai/reme-openclaw-plugin`](./integrations/openclaw.md) |
|
||||||
|
| Claude Code | [Shared HTTP MCP + Skill + Stop Hook](./integrations/claude-code.md) |
|
||||||
|
| Hermes Agent | Memory provider adapter |
|
||||||
|
| Codex or another coding agent | `reme_memory` Skill or MCP |
|
||||||
|
|
||||||
|
## General memory loop
|
||||||
|
|
||||||
|
1. Before answering, call `search` for relevant memory.
|
||||||
|
2. Use `read` on high-value results and `traverse` when relationships matter.
|
||||||
|
3. Retain workspace-relative source paths in the answer.
|
||||||
|
4. At session end, pass source messages to `auto_memory`.
|
||||||
|
5. Let background or scheduled workflows consolidate daily notes into digest memory.
|
||||||
|
|
||||||
|
An empty search result must remain empty; do not present model inference as recalled history.
|
||||||
|
|
||||||
|
## MCP
|
||||||
|
|
||||||
|
The default HTTP service exposes streamable HTTP MCP at `http://127.0.0.1:2333/mcp`. Common tools include `search`,
|
||||||
|
`read`, `traverse`, `list`, `auto_memory`, and `proactive_read`.
|
||||||
|
|
||||||
|
Use `service.jobs` to expose a read-only subset or keep write tools in a separate configuration.
|
||||||
|
|
||||||
|
## CLI and Skill
|
||||||
|
|
||||||
|
`skills/reme_memory/SKILL.md` defines a general workflow for agents that can run local commands: installation checks, service discovery, retrieval, reading, and persistence boundaries.
|
||||||
|
|
||||||
|
It deliberately avoids silently modifying Python environments, stopping unknown processes on port conflicts, writing recalled tool output back as conversation source, or persisting credentials.
|
||||||
|
|
||||||
|
## DeepSeek Harness
|
||||||
|
|
||||||
|
Install the self-contained [DeepSeek Harness plugin](./integrations/dsh.md):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dsh plugin --profile web add @agentscope-ai/reme-dsh-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
Release links: [Awesome DSH Plugin](https://awesome-dsh-plugin.com/p/agentscope-ai/ReMe--integrations-dsh/) and
|
||||||
|
[npm](https://www.npmjs.com/package/@agentscope-ai/reme-dsh-plugin).
|
||||||
|
|
||||||
|
It injects long-term-memory usage guidance into new root-agent sessions and exposes the read-only `reme_search` tool;
|
||||||
|
it does not preload the full memory history into the prompt. Completed user/assistant turns can be submitted to
|
||||||
|
`auto_memory` in background batches, while a timezone-aware schedule runs `auto_dream` to consolidate daily notes.
|
||||||
|
|
||||||
|
DSH settings configure the endpoint, guidance language, search limits, capture interval, root-agent filtering, and
|
||||||
|
consolidation schedule. The ReMe Status page exposes Overview, Auto Memory, Memory Consolidation, Components, Journal,
|
||||||
|
and Personal Knowledge Base views. Runtime counters are diagnostic state; workspace Markdown remains the durable source
|
||||||
|
of truth.
|
||||||
|
|
||||||
|
## OpenClaw
|
||||||
|
|
||||||
|
Install the independently published [OpenClaw plugin](./integrations/openclaw.md):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw plugins install clawhub:@agentscope-ai/reme-openclaw-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
Release links: [ClawHub](https://clawhub.ai/agentscope-ai/plugins/reme-openclaw-plugin) and
|
||||||
|
[npm](https://www.npmjs.com/package/@agentscope-ai/reme-openclaw-plugin). The plugin provides its own host-specific
|
||||||
|
ReMe HTTP boundary and release lifecycle.
|
||||||
|
|
||||||
|
## Claude Code
|
||||||
|
|
||||||
|
The [Claude Code plugin](./integrations/claude-code.md) connects every Claude Code window to one ReMe HTTP process at
|
||||||
|
`http://127.0.0.1:2333/mcp` by default. The `reme-memory` Skill selects among semantic `search`, topological `traverse`,
|
||||||
|
and state-oriented `daily_list` / `frontmatter_read`, then reads and cites the relevant workspace paths.
|
||||||
|
|
||||||
|
On Stop, the hook passes only the Claude Code `session_id` to the server-side `auto_memory_cc` job. On POSIX systems it
|
||||||
|
detaches the potentially long model call so Claude Code can stop immediately; unreachable-service and other best-effort
|
||||||
|
failures are written to the plugin log instead of blocking the host. ReMe resolves the local transcript, and repeated
|
||||||
|
Stop events with no new messages do not create duplicate memory.
|
||||||
|
|
||||||
|
## Hermes Agent
|
||||||
|
|
||||||
|
`integrations/hermes_agent/` provides a memory provider with HTTP and embedded modes. It recalls context before model calls and asynchronously invokes `auto_memory` after each turn. Its `config_schema.py` is rendered by Hermes' generic memory settings UI.
|
||||||
|
|
||||||
|
## Production guidance
|
||||||
|
|
||||||
|
- choose a stable absolute `workspace_dir`;
|
||||||
|
- reuse a service discovered by `reme find_reme`;
|
||||||
|
- treat `reme help` as the active Job contract;
|
||||||
|
- apply timeouts and failure logging to writes;
|
||||||
|
- do not block the host's core response path when memory is temporarily unavailable;
|
||||||
|
- use authentication, TLS, and a minimal Job allowlist for remote access.
|
||||||
|
|
@ -96,7 +96,7 @@ The corresponding automatic flows are [Auto Memory](./auto_memory.md), [Auto Res
|
||||||
│ ├── YYYY-MM-DD.md # index page for the day
|
│ ├── YYYY-MM-DD.md # index page for the day
|
||||||
│ └── YYYY-MM-DD/
|
│ └── YYYY-MM-DD/
|
||||||
│ ├── <generated_name>.md # topic-named conversation or resource card
|
│ ├── <generated_name>.md # topic-named conversation or resource card
|
||||||
│ └── interests.yaml # proactive interest topics generated by auto_dream
|
│ └── interests.yaml # proactive interest topics generated by proactive refresh
|
||||||
└── digest/ # deeply processed layer; reusable personal facts, procedures, and knowledge nodes
|
└── digest/ # deeply processed layer; reusable personal facts, procedures, and knowledge nodes
|
||||||
├── personal/
|
├── personal/
|
||||||
│ └── <memory>.md # user profile, preferences, and durable personal facts
|
│ └── <memory>.md # user profile, preferences, and durable personal facts
|
||||||
|
|
@ -237,8 +237,9 @@ FileLink
|
||||||
|
|
||||||
Older documents containing wrappers such as `related:: [[path]]`,
|
Older documents containing wrappers such as `related:: [[path]]`,
|
||||||
`- related:: [[path]]`, or `[related:: [[path]]]` remain readable. ReMe ignores the surrounding text and indexes the
|
`- related:: [[path]]`, or `[related:: [[path]]]` remain readable. ReMe ignores the surrounding text and indexes the
|
||||||
inner `[[path]]` as an ordinary link. After upgrading from a version that stored typed links, run `reme reindex`
|
inner `[[path]]` as an ordinary link. Graph changes are applied when source files pass through the normal ingestion
|
||||||
once to rebuild the derived graph without the removed relationship field.
|
path. `reme reindex` only rebuilds BM25 and embedding indexes from existing chunks; it does not reparse files or rebuild
|
||||||
|
the derived graph.
|
||||||
|
|
||||||
### Sources and Relationships
|
### Sources and Relationships
|
||||||
|
|
||||||
|
|
@ -388,3 +389,8 @@ This lets the agent see not only an isolated paragraph but also its structural p
|
||||||
|
|
||||||
Non-Markdown files use `DefaultFileChunker` by default. It splits by byte size and preserves a small overlap. For
|
Non-Markdown files use `DefaultFileChunker` by default. It splits by byte size and preserves a small overlap. For
|
||||||
Markdown, the chunker also avoids cutting `[[wikilinks]]` in the middle.
|
Markdown, the chunker also avoids cutting `[[wikilinks]]` in the middle.
|
||||||
|
|
||||||
|
`DefaultFileChunker` and `MarkdownFileChunker` decode files with their configured `encoding` and normalize platform
|
||||||
|
newlines to LF before indexing. Their default `invalid_encoding_policy: replace` keeps decodable content searchable
|
||||||
|
when a source contains invalid bytes, without modifying the source file. Set `invalid_encoding_policy: strict` on a
|
||||||
|
chunker component to reject such files instead.
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
Memory Search is ReMe's memory retrieval entry point. The default background loop continuously builds Markdown under
|
Memory Search is ReMe's memory retrieval entry point. The default background loop continuously builds Markdown under
|
||||||
`daily/` and `digest/` into a searchable chunk index and wikilink graph. At query time, it first recalls the most
|
`daily/` and `digest/` into a searchable chunk index and wikilink graph. At query time, it first recalls the most
|
||||||
relevant fragments and then expands context along the bidirectional links of the files containing those fragments.
|
relevant fragments and then expands context along the bidirectional links of the files containing those fragments.
|
||||||
`reme reindex` has a broader rebuild scope that also scans `resource/` and JSONL; it is intentionally different from the
|
`reme reindex` rebuilds derived BM25 and embedding indexes from the authoritative in-memory `file_chunks`; it does not
|
||||||
live watcher.
|
rescan workspace files, rechunk content, or rewrite the wikilink graph.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="../figure/auto-index-and-memory-search.svg" alt="ReMe Auto Index and Memory Search indexing, recall, fusion, and link expansion" width="92%">
|
<img src="../figure/auto-index-and-memory-search.svg" alt="ReMe Auto Index and Memory Search indexing, recall, fusion, and link expansion" width="92%">
|
||||||
|
|
@ -29,11 +29,8 @@ The default `index_update_loop` watches two memory directories:
|
||||||
- `digest_dir`: long-term distilled digest nodes.
|
- `digest_dir`: long-term distilled digest nodes.
|
||||||
|
|
||||||
The live watcher handles only the `md` suffix. A separate `resource_watch_loop` watches `resource_dir`, and Auto
|
The live watcher handles only the `md` suffix. A separate `resource_watch_loop` watches `resource_dir`, and Auto
|
||||||
Resource turns those inputs into daily cards that enter the live index. When `reme reindex` is run manually, its
|
Resource turns those inputs into daily cards that enter the live index. Manual `reindex` operates on chunks already
|
||||||
configuration scans
|
accepted by those ingestion paths and therefore does not expand the set of searched files.
|
||||||
`daily_dir`, `digest_dir`, and `resource_dir` for `md` and `jsonl`; Markdown uses the `markdown` chunker and JSONL uses
|
|
||||||
the
|
|
||||||
`jsonl` chunker.
|
|
||||||
|
|
||||||
## How the Index Is Built
|
## How the Index Is Built
|
||||||
|
|
||||||
|
|
@ -114,8 +111,29 @@ It combines three kinds of capability:
|
||||||
| `embedding_store` | Disabled | When enabled, generate embeddings for chunks and support vector recall. |
|
| `embedding_store` | Disabled | When enabled, generate embeddings for chunks and support vector recall. |
|
||||||
|
|
||||||
Out of the box, search therefore uses primarily BM25 plus link expansion. After setting `embedding_store: default`,
|
Out of the box, search therefore uses primarily BM25 plus link expansion. After setting `embedding_store: default`,
|
||||||
`SearchStep` runs vector and keyword recall together. Additionally, switching the `file_store` `backend` from `local` to
|
`SearchStep` runs vector and keyword recall together.
|
||||||
`faiss` upgrades vector retrieval from a linear scan to a FAISS HNSW index, offering faster recall at scale.
|
|
||||||
|
The embedding store accepts `health_check_timeout` for its startup probe. A temporary failure skips the current vector
|
||||||
|
backfill while keeping BM25 available; a later successful provider request resumes the missing-vector backfill
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
Embedded integrations that have already verified a provider can call `resume_embedding(verified=True)` to repair
|
||||||
|
missing vectors in the same vector space. Vector-space changes must use the explicit `reindex` job with
|
||||||
|
`scope: embedding`; vector search remains unavailable until that job finishes successfully.
|
||||||
|
Use `scope: bm25` to rebuild only keyword search, or `scope: tag` to rebuild the optional tag index from the current
|
||||||
|
file graph. `scope: all` rebuilds BM25 first, then embeddings, and finally tags. BM25 and embedding rebuilds use the
|
||||||
|
current `file_chunks` snapshot; the tag rebuild uses `FileNode` frontmatter from the file graph.
|
||||||
|
|
||||||
|
## Vector Index Backends
|
||||||
|
|
||||||
|
With embeddings enabled, `file_store.default.backend` can be `local`, `zvec`, or `faiss`. `local` scans vectors linearly;
|
||||||
|
`zvec` uses an in-process HNSW index with native vector updates and deletes; `faiss` uses a FAISS HNSW index. The latter
|
||||||
|
two store rebuildable vector indexes. Memory files and ReMe's chunk data remain the source of truth.
|
||||||
|
|
||||||
|
To use Zvec or FAISS, first configure `as_embedding` and `embedding_store` as shown in
|
||||||
|
[Configuration](./configuration.md#embeddings). Then set `file_store.default.embedding_store` to `default` and
|
||||||
|
`file_store.default.backend` to `zvec` or `faiss`. The `core` installation includes both dependencies; the default
|
||||||
|
configuration still uses `local` with embeddings disabled.
|
||||||
|
|
||||||
## How to Search
|
## How to Search
|
||||||
|
|
||||||
|
|
|
||||||
102
docs/en/operations.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: Diagnostics, Backup, and Recovery
|
||||||
|
description: ReMe health checks, logs, index maintenance, workspace backup, migration, and recovery.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Diagnostics, Backup, and Recovery
|
||||||
|
|
||||||
|
ReMe recovery protects user-owned workspace files and rebuilds catalogs, indexes, and graphs from those sources. Never delete or rewrite user memory merely to repair derived state.
|
||||||
|
|
||||||
|
## Quick diagnosis
|
||||||
|
|
||||||
|
Run these in order:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme find_reme
|
||||||
|
reme version
|
||||||
|
reme health_check
|
||||||
|
reme status
|
||||||
|
reme app_config
|
||||||
|
```
|
||||||
|
|
||||||
|
- `find_reme` confirms the actual host, port, and PID;
|
||||||
|
- `version` verifies that the CLI reaches the service;
|
||||||
|
- `health_check` reports component health;
|
||||||
|
- `status` estimates stateful component memory and process RSS;
|
||||||
|
- `app_config` returns the effective configuration with secrets redacted.
|
||||||
|
|
||||||
|
## Logs and common symptoms
|
||||||
|
|
||||||
|
`log_to_console` and `log_to_file` control logging. For startup failures, inspect the first exception rather than later client connection errors.
|
||||||
|
|
||||||
|
| Symptom | Check first |
|
||||||
|
|---|---|
|
||||||
|
| CLI cannot find ReMe | `reme find_reme`, process state, startup directory, and port |
|
||||||
|
| Automatic memory fails | LLM backend, model, API key, and base URL |
|
||||||
|
| Search is BM25-only | Whether an embedding store is connected to `file_store` |
|
||||||
|
| New files are absent | Directory, extension, watcher, and `health_check` |
|
||||||
|
| Studio fails but API works | Installed web extra, static path, and browser console |
|
||||||
|
| Installed plugin is unavailable | Python interpreter, `plugins` configuration, and service restart |
|
||||||
|
|
||||||
|
## Index maintenance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme reindex scope=all
|
||||||
|
reme reindex scope=bm25
|
||||||
|
reme reindex scope=embedding
|
||||||
|
```
|
||||||
|
|
||||||
|
`reindex` rebuilds BM25 and/or embedding indexes from the current `file_chunks`. It does not scan the workspace, rechunk files, or rebuild the wikilink graph. Diagnose the watcher first when ingestion is the problem.
|
||||||
|
|
||||||
|
Rebuild a daily index page separately:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme daily_reindex date=2026-09-04
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup
|
||||||
|
|
||||||
|
Stop writes or stop the service, then back up the complete workspace. The most important sources are:
|
||||||
|
|
||||||
|
- `session/` for conversation sources;
|
||||||
|
- `resource/` for external resources;
|
||||||
|
- `daily/` for daily memory;
|
||||||
|
- `digest/` for consolidated memory.
|
||||||
|
|
||||||
|
`metadata/` contains indexes, graphs, and catalogs. Backing it up accelerates restoration, but it is not the sole source of truth.
|
||||||
|
|
||||||
|
Use an explicit, stable absolute `workspace_dir` for durable deployments rather than relying on an incidental `.reme/` under the current directory.
|
||||||
|
|
||||||
|
## Migrate a workspace
|
||||||
|
|
||||||
|
1. Stop the old service to prevent writes during the copy.
|
||||||
|
2. Copy the complete workspace while preserving timestamps.
|
||||||
|
3. Start with the new absolute path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start workspace_dir=/new/location/reme-memory
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Run `health_check`, `status`, and a representative `search`.
|
||||||
|
5. Rebuild embeddings if their model or dimensions changed.
|
||||||
|
|
||||||
|
Do not push a workspace containing private conversations to a public repository.
|
||||||
|
|
||||||
|
## Recover derived state
|
||||||
|
|
||||||
|
Do not remove anything until a backup exists. Then:
|
||||||
|
|
||||||
|
1. preserve `session/`, `resource/`, `daily/`, and `digest/`;
|
||||||
|
2. record the effective configuration and component backends;
|
||||||
|
3. verify that the failure is limited to `metadata/`;
|
||||||
|
4. move suspect derived state to an isolated backup location;
|
||||||
|
5. restart with the same configuration and let watchers rebuild;
|
||||||
|
6. validate search, graph traversal, and daily indexes.
|
||||||
|
|
||||||
|
The internal layout of metadata files is not a public automation contract.
|
||||||
|
|
||||||
|
## Concurrent editing
|
||||||
|
|
||||||
|
When Studio or an editor saves a complete file, pass the mtime from `stat` as `save.expected_mtime`. A save then fails if another actor changed the file after it was opened, avoiding silent overwrites.
|
||||||
|
|
||||||
|
File Jobs enforce workspace containment and per-path locking. Do not bypass them to write arbitrary absolute paths.
|
||||||
101
docs/en/plugin_development.md
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
title: Plugin Development
|
||||||
|
description: Create, register, configure, test, and publish a ReMe plugin.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plugin Development
|
||||||
|
|
||||||
|
A ReMe plugin is a regular Python distribution exposed through the `reme.plugins` entry-point group. Its package-level `plugin.yaml` can register Step and Component backends and provide default Application configuration.
|
||||||
|
|
||||||
|
## Minimal structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-plugin/
|
||||||
|
├── pyproject.toml
|
||||||
|
└── src/my_plugin/
|
||||||
|
├── __init__.py
|
||||||
|
├── plugin.yaml
|
||||||
|
└── steps.py
|
||||||
|
```
|
||||||
|
|
||||||
|
`pyproject.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[project.entry-points."reme.plugins"]
|
||||||
|
my-plugin = "my_plugin"
|
||||||
|
```
|
||||||
|
|
||||||
|
`plugin.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: my-plugin
|
||||||
|
backends:
|
||||||
|
my_step: my_plugin.steps:MyStep
|
||||||
|
application_defaults:
|
||||||
|
jobs:
|
||||||
|
my_action:
|
||||||
|
backend: base
|
||||||
|
description: Run my plugin action
|
||||||
|
parameters:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
text: { type: string }
|
||||||
|
required: [text]
|
||||||
|
steps:
|
||||||
|
- backend: my_step
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implement a Step
|
||||||
|
|
||||||
|
```python
|
||||||
|
from reme.components.component_registry import R
|
||||||
|
from reme.steps.base_step import BaseStep
|
||||||
|
|
||||||
|
|
||||||
|
@R.register("my_step")
|
||||||
|
class MyStep(BaseStep):
|
||||||
|
async def execute(self):
|
||||||
|
self.context.response.answer = self.context.data["text"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Step instances belong to one Job invocation. Put shared in-memory state under a narrow `app_context.metadata` key. Promote state that needs lifecycle, locking, or persistence to a Component or workspace file.
|
||||||
|
|
||||||
|
## Configuration merge
|
||||||
|
|
||||||
|
`application_defaults` is a partial `ApplicationConfig`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
plugin defaults < selected/default config < CLI overrides
|
||||||
|
```
|
||||||
|
|
||||||
|
Plugins must not rewrite user configuration. Their backends enter an Application-local registry only when the plugin appears in that Application's `plugins` list.
|
||||||
|
|
||||||
|
## Local validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins validate ./path/to/my-plugin
|
||||||
|
reme plugins install ./path/to/my-plugin --editable
|
||||||
|
reme plugins list
|
||||||
|
reme plugins show my-plugin
|
||||||
|
reme start plugins='["my-plugin"]'
|
||||||
|
reme my_action text=hello
|
||||||
|
```
|
||||||
|
|
||||||
|
Validation imports plugin code, so run it only for trusted sources.
|
||||||
|
|
||||||
|
## Test boundaries
|
||||||
|
|
||||||
|
- create workspaces with `tmp_path`;
|
||||||
|
- mock network, model, and subprocess boundaries;
|
||||||
|
- verify disabled plugins do not mutate the built-in registry;
|
||||||
|
- verify plugin defaults and explicit configuration precedence;
|
||||||
|
- keep tasks, clients, and executors under Component lifecycle;
|
||||||
|
- never delete or rewrite user source files to repair derived state.
|
||||||
|
|
||||||
|
The repository's Daily Paper, Auto Fin, LME, and BEAM plugins are complete examples.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
Legacy Python Plugin descriptors and the `reme.configs` entry point remain supported during migration, but new plugins should use `plugin.yaml`. Enablement always belongs to an Application rather than a process-global switch.
|
||||||
|
|
||||||
|
See [Plugin Management](./plugin_management.md) for installation, upgrades, and removal.
|
||||||
236
docs/en/plugin_management.md
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
# Plugin Management
|
||||||
|
|
||||||
|
ReMe plugins are ordinary Python distributions discovered through the `reme.plugins` entry-point group. Installing a
|
||||||
|
plugin makes it available to the current Python environment; it does not enable the plugin in every ReMe application.
|
||||||
|
|
||||||
|
Keep these two operations separate:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reme plugins install ... install a package into the current Python environment
|
||||||
|
plugins: [auto-fin] enable an installed plugin for one Application
|
||||||
|
```
|
||||||
|
|
||||||
|
Plugin package management is local-only. It does not run through a ReMe HTTP or MCP service and never edits application
|
||||||
|
configuration files automatically.
|
||||||
|
|
||||||
|
A typical plugin workflow has three stages:
|
||||||
|
|
||||||
|
1. Install ReMe and the plugin distribution.
|
||||||
|
2. Configure the plugin's runtime environment as described in the
|
||||||
|
[ReMe model-configuration guide](../../README.md#optional-model-configuration).
|
||||||
|
3. Start an Application with the plugin explicitly enabled, for example
|
||||||
|
`reme start plugins='["auto-fin"]'`.
|
||||||
|
|
||||||
|
## List installed plugins
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list
|
||||||
|
```
|
||||||
|
|
||||||
|
The table shows the plugin entry-point name, Python distribution, version, and plugin contract:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PLUGIN DISTRIBUTION VERSION FORMAT
|
||||||
|
-------- ------------- ------- --------
|
||||||
|
auto-fin reme-auto-fin X.Y.Z manifest
|
||||||
|
```
|
||||||
|
|
||||||
|
`manifest` plugins use the current package-level `plugin.yaml` contract. `legacy` plugins use the compatible Python
|
||||||
|
descriptor contract.
|
||||||
|
|
||||||
|
A manifest separates backend registration from application configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
backends:
|
||||||
|
example_step: example_plugin.steps:ExampleStep
|
||||||
|
|
||||||
|
application_defaults:
|
||||||
|
jobs:
|
||||||
|
example:
|
||||||
|
backend: base
|
||||||
|
steps:
|
||||||
|
- backend: example_step
|
||||||
|
```
|
||||||
|
|
||||||
|
`application_defaults` is a partial `ApplicationConfig`. It is kept below the manifest's `backends` namespace because
|
||||||
|
backend import declarations are part of plugin discovery and are not application configuration.
|
||||||
|
|
||||||
|
Use JSON when another local tool needs structured output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list --json
|
||||||
|
```
|
||||||
|
|
||||||
|
To compare installed plugins with one application config:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list --config default
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional `ENABLED` column reflects only the `plugins` list resolved from that config. A command-line override used
|
||||||
|
by another running process is not a global enable state.
|
||||||
|
|
||||||
|
## Install a plugin package
|
||||||
|
|
||||||
|
Install a published distribution by its package name:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins install your-plugin-package
|
||||||
|
```
|
||||||
|
|
||||||
|
Install or upgrade a pinned version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins install 'your-plugin-package==X.Y.Z'
|
||||||
|
reme plugins install your-plugin-package --upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
Install a local plugin project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins install ./plugins/auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
Use editable mode while developing it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins install ./plugins/auto-fin --editable
|
||||||
|
```
|
||||||
|
|
||||||
|
ReMe invokes pip through the same Python interpreter that runs the `reme` command. Pip remains responsible for package
|
||||||
|
resolution, downloads, dependency changes, and build execution. Install only packages and local projects you trust.
|
||||||
|
|
||||||
|
After installation, confirm the discovered plugin name:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list
|
||||||
|
reme plugins validate auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inspect a plugin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins show auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
For a manifest plugin, the result includes its registered backend names and default Job names. JSON output is also
|
||||||
|
available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins show auto-fin --json
|
||||||
|
```
|
||||||
|
|
||||||
|
`show` identifies the package contract without constructing a ReMe Application.
|
||||||
|
|
||||||
|
## Validate a plugin
|
||||||
|
|
||||||
|
Validate an installed plugin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins validate auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
Validate a local project before installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins validate ./plugins/auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
Validation checks the entry point, `plugin.yaml`, backend imports and component types, registry collisions, merged
|
||||||
|
`application_defaults`, and the resulting `ApplicationConfig`. Validation imports plugin backend modules, so run it
|
||||||
|
only for trusted code.
|
||||||
|
|
||||||
|
## Enable a plugin in a service
|
||||||
|
|
||||||
|
Installation alone does not load plugin code into an Application. Enable plugins explicitly in configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
plugins:
|
||||||
|
- auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
Or add them for one service launch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start plugins='["auto-fin"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
When `config` is omitted, ReMe loads `default.yaml`. The plugin's `application_defaults` are merged below that config,
|
||||||
|
so explicit config values and CLI overrides win. This mapping is an `ApplicationConfig` fragment, not a separate
|
||||||
|
configuration schema. The plugin backends are registered only in that Application's local registry.
|
||||||
|
|
||||||
|
After the default HTTP service starts, access plugin Jobs through ReMe's CLI client or HTTP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme auto_fin topics="黄金,AI,存储芯片"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s http://127.0.0.1:2333/auto_fin \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"topics":"黄金,AI,存储芯片"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
When the application uses an MCP service, service-enabled plugin Jobs appear as MCP tools instead.
|
||||||
|
|
||||||
|
Custom application configs must provide the plugin's runtime dependencies, including an `agent_wrapper.default`, a
|
||||||
|
`file_store.default` with an enabled tag index, and the `search`, `read`, `list_tags`, `frontmatter_read`, and
|
||||||
|
`frontmatter_update` Jobs used by Auto Fin and automatic tagging.
|
||||||
|
|
||||||
|
## Benchmark application presets
|
||||||
|
|
||||||
|
The [LME](../../plugins/lme/README.md) and [BEAM](../../plugins/beam/README.md) plugins
|
||||||
|
register their backends and plugin-owned Jobs in `plugin.yaml`. ReMe's built-in `benchmark`
|
||||||
|
preset provides the shared core Jobs and components without inheriting `default`, so default
|
||||||
|
background and cron jobs are not included. Install the selected benchmark plugin, then use
|
||||||
|
`config=benchmark` together with `plugins=["lme"]` or `plugins=["beam"]`. The repository's
|
||||||
|
benchmark runners enable the corresponding installed plugin automatically; editable installation
|
||||||
|
keeps local plugin changes visible. Dataset runners remain under `benchmark/`.
|
||||||
|
|
||||||
|
## Uninstall a plugin
|
||||||
|
|
||||||
|
Use the plugin entry-point name, not necessarily the distribution name:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins uninstall auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
Skip pip's confirmation prompt when needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins uninstall auto-fin --yes
|
||||||
|
```
|
||||||
|
|
||||||
|
ReMe resolves `auto-fin` to the distribution that provides it, such as `reme-auto-fin`. If one distribution provides
|
||||||
|
multiple plugin entry points, the command lists the other plugins that will also be removed.
|
||||||
|
|
||||||
|
Uninstallation does not rewrite user configuration. Remove the plugin from relevant `plugins` lists yourself;
|
||||||
|
otherwise the next Application startup fails explicitly because the configured plugin is no longer installed. Restart
|
||||||
|
already-running ReMe processes after installing, upgrading, or uninstalling packages.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Plugin is installed but unavailable
|
||||||
|
|
||||||
|
Check that the `reme` command and pip package share one Python interpreter:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list
|
||||||
|
python -c 'import sys; print(sys.executable)'
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `reme plugins install` avoids the most common interpreter mismatch because it runs `python -m pip` with ReMe's
|
||||||
|
own interpreter.
|
||||||
|
|
||||||
|
### Plugin is installed but not loaded
|
||||||
|
|
||||||
|
Add its entry-point name to the Application's `plugins` list. ReMe intentionally has no global enable/disable state.
|
||||||
|
|
||||||
|
### Startup reports that the plugin is not installed
|
||||||
|
|
||||||
|
The active config still enables a missing plugin. Reinstall it or remove the corresponding name from `plugins`.
|
||||||
|
|
||||||
|
### Changes are not visible in a running service
|
||||||
|
|
||||||
|
Plugin discovery and backend registration happen during Application construction. Restart the service after changing
|
||||||
|
installed packages.
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Proactive
|
# Proactive
|
||||||
|
|
||||||
`proactive` is ReMe's interface for reading proactive memory. It does not reanalyze daily notes or call an LLM. It only
|
`proactive_read` is ReMe's interface for reading proactive memory. It does not reanalyze daily notes or call an LLM. It
|
||||||
reads the current day's interest topics written by `auto_dream`:
|
reads interest topics written by the independent proactive refresh flow:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
daily/<date>/interests.yaml
|
daily/<date>/interests.yaml
|
||||||
|
|
@ -10,56 +10,131 @@ daily/<date>/interests.yaml
|
||||||
A host agent can use it to learn "what is worth proactive attention today," then decide whether to remind the user, ask
|
A host agent can use it to learn "what is worth proactive attention today," then decide whether to remind the user, ask
|
||||||
a follow-up question, recommend a next step, or produce a proactive insight.
|
a follow-up question, recommend a next step, or produce a proactive insight.
|
||||||
|
|
||||||
`interests.yaml` is generated by the Topics stage of [Auto Dream](./auto_dream.md). `proactive` only reads and exposes
|
`interests.yaml` is generated by the proactive refresh pipeline, scheduled by `proactive_refresh_cron` by default.
|
||||||
the result.
|
`proactive_read` only reads and exposes the result; Auto Dream is an independent daily-to-digest flow and does not read
|
||||||
|
or write proactive state.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The default configuration is in `reme/config/default.yaml`:
|
The default configuration is in `reme/config/default.yaml`. It defines the same refresh steps twice: a local-only
|
||||||
|
one-shot job for maintenance and debugging, and the scheduled job that runs every day at 18:00 in the application
|
||||||
|
timezone:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
proactive:
|
proactive_refresh:
|
||||||
|
backend: base
|
||||||
|
enable_serve: false
|
||||||
|
steps: &proactive_refresh_steps
|
||||||
|
- backend: proactive_extract_step
|
||||||
|
file_catalog: proactive
|
||||||
|
scan_days: 2
|
||||||
|
carry_forward_days: 14
|
||||||
|
max_carry_forward_topics: 20
|
||||||
|
llm_timeout_seconds: 300
|
||||||
|
max_chars_per_file: 60000
|
||||||
|
max_total_chars: 300000
|
||||||
|
- backend: proactive_topics_step
|
||||||
|
known_threshold: 0.85
|
||||||
|
known_threshold_calibrated_for: text-embedding-v4@1024
|
||||||
|
min_push_confidence: 0.5
|
||||||
|
max_topics: 10
|
||||||
|
- backend: proactive_plan_step
|
||||||
|
- backend: proactive_agenda_step
|
||||||
|
- backend: proactive_finish_step
|
||||||
|
file_catalog: proactive
|
||||||
|
|
||||||
|
proactive_refresh_cron:
|
||||||
|
backend: cron
|
||||||
|
cron: "0 18 * * *"
|
||||||
|
steps: *proactive_refresh_steps
|
||||||
|
```
|
||||||
|
|
||||||
|
The anchor above is only a compact illustration; `default.yaml` spells out both step lists explicitly. The read job is:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
proactive_read:
|
||||||
backend: base
|
backend: base
|
||||||
description: "Proactive: read daily/<date>/interests.yaml and expose the latest user-interest topics."
|
description: "Proactive: read daily/<date>/interests.yaml and expose the latest user-interest topics."
|
||||||
parameters:
|
parameters:
|
||||||
date:
|
type: object
|
||||||
type: string
|
properties:
|
||||||
default: ""
|
date:
|
||||||
include_content:
|
type: string
|
||||||
type: boolean
|
default: ""
|
||||||
default: true
|
include_content:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
horizon_days:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
min_confidence:
|
||||||
|
type: number
|
||||||
|
default: 0.4
|
||||||
steps:
|
steps:
|
||||||
- backend: proactive_step
|
- backend: proactive_step
|
||||||
|
min_confidence: 0.4
|
||||||
```
|
```
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
| Parameter | Purpose |
|
| Parameter | Purpose |
|
||||||
|-------------------|-------------------------------------------------------------------------------------------|
|
|-------------------|------------------------------------------------------------------------------------------------------|
|
||||||
| `date` | Date to read in `YYYY-MM-DD` format. When empty, use today in the application's timezone. |
|
| `date` | Date to read in `YYYY-MM-DD` format. When empty, use today in the application's timezone. |
|
||||||
| `include_content` | Whether to return the raw YAML in the answer and metadata. Defaults to `true`. |
|
| `include_content` | Whether to return the raw YAML in the answer and metadata. Defaults to `true`. |
|
||||||
|
| `horizon_days` | Read one day's exposure file, or use the truth source for a wider evidence horizon. Defaults to `1`. |
|
||||||
|
| `min_confidence` | Minimum topic confidence to return. Defaults to `0.4`; legacy topics use `0.5`. |
|
||||||
|
|
||||||
|
### Refresh cost, files, and opt-out
|
||||||
|
|
||||||
|
When no daily Markdown note changed, refresh exits before calling an LLM and does not create a new exposure file. With
|
||||||
|
changed material, extraction normally makes one LLM call and may retry once after an unusable reply. If push candidates
|
||||||
|
remain, planning makes one additional call; agenda generation makes one more when there are multiple candidates. A
|
||||||
|
refresh therefore makes at most four LLM calls with the default chain.
|
||||||
|
|
||||||
|
The refresh pipeline maintains the rebuildable `daily/_proactive.yaml` truth source, writes
|
||||||
|
`daily/<date>/interests.yaml`, and advances the independent `proactive` file catalog. Auto Dream does not read or write
|
||||||
|
any of those proactive artifacts.
|
||||||
|
|
||||||
|
To disable automatic refresh, use an explicit application config that omits the `proactive_refresh_cron` job. Keep the
|
||||||
|
local-only `proactive_refresh` job if you still want on-demand maintenance. Because it has `enable_serve: false`, it is
|
||||||
|
not exposed through HTTP or MCP.
|
||||||
|
|
||||||
## Input Contract
|
## Input Contract
|
||||||
|
|
||||||
A typical file looks like this:
|
A current proactive-refresh file looks like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
version: 2
|
||||||
date: 2026-06-20
|
date: 2026-06-20
|
||||||
topic_count: 3
|
generated_at: 2026-06-20T18:00:00+08:00
|
||||||
diversity_days: 7
|
push: true
|
||||||
topics:
|
topics:
|
||||||
- title: Quality regression in the memory retrieval pipeline
|
- id: baa88ad49cb2
|
||||||
|
title: Quality regression in the memory retrieval pipeline
|
||||||
reason: The user has recently made repeated changes to search, node_search, and dream integration.
|
reason: The user has recently made repeated changes to search, node_search, and dream integration.
|
||||||
|
kind: follow_up
|
||||||
|
confidence: 0.86
|
||||||
|
first_seen: 2026-06-20
|
||||||
|
last_evidence_at: 2026-06-20
|
||||||
evidence: daily/2026-06-20/session.md
|
evidence: daily/2026-06-20/session.md
|
||||||
keywords:
|
|
||||||
- memory search
|
|
||||||
- auto dream
|
|
||||||
paths:
|
paths:
|
||||||
- daily/2026-06-20/session.md
|
- daily/2026-06-20/session.md
|
||||||
|
agenda:
|
||||||
|
- topic_id: baa88ad49cb2
|
||||||
|
title: Quality regression in the memory retrieval pipeline
|
||||||
|
scenario_type: resume_task
|
||||||
|
opener: Review the latest retrieval regression before the next release.
|
||||||
|
next_action: Compare the failing query against the previous index snapshot.
|
||||||
|
preconditions: []
|
||||||
|
delivery: in_conversation
|
||||||
|
linked_memory: []
|
||||||
|
order_reason: Recent evidence and a concrete next action.
|
||||||
|
suppressed: []
|
||||||
```
|
```
|
||||||
|
|
||||||
Only the `topics` list is parsed into structured results. Every topic requires at least `title` and `reason`;
|
Current v2 topics include stable identity, kind, confidence, evidence dates, and source paths. The reader also accepts
|
||||||
`evidence`, `keywords`, and `paths` are supporting fields.
|
legacy v1 files containing `title`, `reason`, `evidence`, `keywords`, and `paths`; missing v2 confidence falls back to
|
||||||
|
`0.5`.
|
||||||
|
|
||||||
## Return Value
|
## Return Value
|
||||||
|
|
||||||
|
|
@ -76,6 +151,14 @@ metadata:
|
||||||
| `skipped` | `true` when the file does not exist. |
|
| `skipped` | `true` when the file does not exist. |
|
||||||
| `error` | Read or parse error. |
|
| `error` | Read or parse error. |
|
||||||
| `summary` | Short summary. |
|
| `summary` | Short summary. |
|
||||||
|
| `agenda` | Today's proactive agenda (optional, v2 files only). |
|
||||||
|
|
||||||
|
When today's `interests.yaml` was produced by the proactive refresh chain with an agenda,
|
||||||
|
the answer also carries an `agenda` field: the ordered agenda items, each with `topic_id`,
|
||||||
|
`title`, `scenario_type`, `opener` (a natural conversation opener), `next_action` (the
|
||||||
|
minimal executable step), `preconditions`, `delivery`, `linked_memory` and `order_reason`.
|
||||||
|
Agenda items whose topic is resolved or below `min_confidence` are filtered out on read;
|
||||||
|
the field is absent when the file has no agenda.
|
||||||
|
|
||||||
When the file exists and parses successfully, the answer is structured data. For example:
|
When the file exists and parses successfully, the answer is structured data. For example:
|
||||||
|
|
||||||
|
|
@ -84,13 +167,30 @@ When the file exists and parses successfully, the answer is structured data. For
|
||||||
"summary": "Read 1 proactive topic(s) from daily/2026-06-20/interests.yaml",
|
"summary": "Read 1 proactive topic(s) from daily/2026-06-20/interests.yaml",
|
||||||
"topics": [
|
"topics": [
|
||||||
{
|
{
|
||||||
|
"id": "baa88ad49cb2",
|
||||||
"title": "Quality regression in the memory retrieval pipeline",
|
"title": "Quality regression in the memory retrieval pipeline",
|
||||||
"reason": "The user has recently made repeated changes to search, node_search, and dream integration.",
|
"reason": "The user has recently made repeated changes to search, node_search, and dream integration.",
|
||||||
|
"kind": "follow_up",
|
||||||
|
"confidence": 0.86,
|
||||||
|
"first_seen": "2026-06-20",
|
||||||
|
"last_evidence_at": "2026-06-20",
|
||||||
"evidence": "daily/2026-06-20/session.md",
|
"evidence": "daily/2026-06-20/session.md",
|
||||||
"keywords": ["memory search", "auto dream"],
|
|
||||||
"paths": ["daily/2026-06-20/session.md"]
|
"paths": ["daily/2026-06-20/session.md"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"agenda": [
|
||||||
|
{
|
||||||
|
"topic_id": "baa88ad49cb2",
|
||||||
|
"title": "Quality regression in the memory retrieval pipeline",
|
||||||
|
"scenario_type": "resume_task",
|
||||||
|
"opener": "Review the latest retrieval regression before the next release.",
|
||||||
|
"next_action": "Compare the failing query against the previous index snapshot.",
|
||||||
|
"preconditions": [],
|
||||||
|
"delivery": "in_conversation",
|
||||||
|
"linked_memory": [],
|
||||||
|
"order_reason": "Recent evidence and a concrete next action."
|
||||||
|
}
|
||||||
|
],
|
||||||
"content": "date: 2026-06-20\n..."
|
"content": "date: 2026-06-20\n..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -104,44 +204,49 @@ A missing file is not an error. The call succeeds with a skipped result:
|
||||||
Skipped: interests file not found at daily/2026-06-20/interests.yaml
|
Skipped: interests file not found at daily/2026-06-20/interests.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
This lets a host agent treat "there is no dream result for today yet" as a normal empty state.
|
This lets a host agent treat "there is no proactive refresh result for today yet" as a normal empty state.
|
||||||
|
|
||||||
## Running Proactive
|
## Running Proactive
|
||||||
|
|
||||||
CLI:
|
Run one refresh immediately through the normal application lifecycle:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
reme proactive date=2026-06-20
|
reme start job=proactive_refresh date=2026-06-20
|
||||||
|
```
|
||||||
|
|
||||||
|
This command may call the configured LLM and may update `_proactive.yaml`, `interests.yaml`, and the proactive catalog.
|
||||||
|
It does not run Auto Dream.
|
||||||
|
|
||||||
|
Read the generated topics:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme proactive_read date=2026-06-20
|
||||||
```
|
```
|
||||||
|
|
||||||
Omit the raw YAML content:
|
Omit the raw YAML content:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
reme proactive date=2026-06-20 include_content=false
|
reme proactive_read date=2026-06-20 include_content=false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Relationship to auto_dream
|
## Relationship to auto_dream
|
||||||
|
|
||||||
`proactive` is the downstream read step for `auto_dream`:
|
Proactive refresh and Auto Dream consume daily notes independently:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
daily notes
|
daily notes -> auto_dream -> digest
|
||||||
-> auto_dream
|
daily notes -> proactive_refresh_cron -> daily/<date>/interests.yaml -> proactive_read -> host agent
|
||||||
-> daily/<date>/interests.yaml
|
|
||||||
-> proactive
|
|
||||||
-> host agent
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The responsibilities are divided as follows. For the complete Extract, Integrate, Topics, and Finish flow, see
|
The proactive responsibilities are divided as follows:
|
||||||
[Auto Dream](./auto_dream.md):
|
|
||||||
|
|
||||||
| Module | Responsibility |
|
| Module | Responsibility |
|
||||||
|----------------------|--------------------------------------------------------|
|
|--------------------------|--------------------------------------------------------|
|
||||||
| `dream_extract_step` | Extract topic candidates from changed daily inputs. |
|
| `proactive_refresh` | Run the refresh pipeline once from the local CLI. |
|
||||||
| `dream_topics_step` | Deduplicate, select, and write `interests.yaml`. |
|
| `proactive_refresh_cron` | Run the same writer pipeline every day at 18:00. |
|
||||||
| `proactive_step` | Read `interests.yaml` and expose it to the host agent. |
|
| `proactive_step` | Read `interests.yaml` and expose it to the host agent. |
|
||||||
|
|
||||||
`proactive` does not modify files, update a catalog, or decide whether the user should be interrupted. It only provides
|
`proactive_read` does not modify files, update a catalog, or decide whether the user should be interrupted. It only provides
|
||||||
the day's topic material. The caller's product policy determines whether, when, and in what tone to push it to the user.
|
the day's topic material. The caller's product policy determines whether, when, and in what tone to push it to the user.
|
||||||
|
|
||||||
## Failure Modes
|
## Failure Modes
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
|
---
|
||||||
|
title: Quick Start
|
||||||
|
description: Install and start ReMe, then complete a first file, retrieval, and automatic-memory workflow.
|
||||||
|
---
|
||||||
|
|
||||||
# Quick Start
|
# Quick Start
|
||||||
|
|
||||||
|
This page gets one working loop running. See [Configuration](./configuration.md) for the full configuration contract and [Services and Deployment](./services.md) for HTTP or MCP integration.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
ReMe requires Python 3.11+.
|
ReMe requires Python 3.11+.
|
||||||
|
|
@ -15,8 +22,8 @@ Install from source:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/agentscope-ai/ReMe.git
|
git clone https://github.com/agentscope-ai/ReMe.git
|
||||||
cd ReMe
|
cd ReMe
|
||||||
pip install -e packages/reme_ai_studio -e ".[core]"
|
pip install -e reme_studio -e ".[core]"
|
||||||
cd website
|
cd reme_studio
|
||||||
npm ci
|
npm ci
|
||||||
npm run build:static
|
npm run build:static
|
||||||
cd ..
|
cd ..
|
||||||
|
|
@ -27,7 +34,7 @@ The static build step requires Node.js 22.13 or newer and makes Studio available
|
||||||
Installing the `core` extra is recommended. The current code imports the AgentScope wrapper, and self-evolving memory
|
Installing the `core` extra is recommended. The current code imports the AgentScope wrapper, and self-evolving memory
|
||||||
also depends on it.
|
also depends on it.
|
||||||
|
|
||||||
To use agent workflows such as `auto_memory`, `auto_resource`, and `auto_dream`, configure an LLM:
|
To use agent workflows such as `auto_memory`, `auto_resource`, `auto_dream`, and proactive refresh, configure an LLM:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat > .env <<'EOF'
|
cat > .env <<'EOF'
|
||||||
|
|
@ -113,12 +120,15 @@ Related link: [[digest/wiki/search-demo.md]]"
|
||||||
and
|
and
|
||||||
`description` are written to frontmatter.
|
`description` are written to frontmatter.
|
||||||
|
|
||||||
The background watcher builds the index automatically. You can also rebuild it manually:
|
The background watcher ingests workspace files automatically. You can manually rebuild the derived BM25 and embedding
|
||||||
|
indexes from the chunks it has already ingested:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
reme reindex
|
reme reindex
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This command does not scan workspace files, rechunk content, or rebuild the wikilink graph.
|
||||||
|
|
||||||
Search:
|
Search:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -182,8 +192,8 @@ reme auto_memory \
|
||||||
```
|
```
|
||||||
|
|
||||||
After placing external material under `resource/YYYY-MM-DD/` or directly under `resource/`, the default background task
|
After placing external material under `resource/YYYY-MM-DD/` or directly under `resource/`, the default background task
|
||||||
watches
|
watches text resources (`md/txt/json/jsonl/csv/yaml/html`) and image resources
|
||||||
`md/txt/json/jsonl/csv/yaml/html`. You can also trigger processing manually:
|
(`png/jpg/jpeg/webp/gif/bmp/tiff/heic`). You can also trigger processing manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
reme auto_resource changes='[{"path":"resource/2026-06-20/report.md","change":"added"}]'
|
reme auto_resource changes='[{"path":"resource/2026-06-20/report.md","change":"added"}]'
|
||||||
|
|
@ -193,7 +203,7 @@ Distill daily notes into long-term digest memory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
reme auto_dream date=2026-06-20
|
reme auto_dream date=2026-06-20
|
||||||
reme proactive date=2026-06-20
|
reme proactive_read date=2026-06-20
|
||||||
```
|
```
|
||||||
|
|
||||||
These flows require a working LLM. Without an LLM configuration, start with basic capabilities such as `write`, `read`,
|
These flows require a working LLM. Without an LLM configuration, start with basic capabilities such as `write`, `read`,
|
||||||
|
|
@ -234,3 +244,5 @@ You can also specify a YAML or JSON configuration file:
|
||||||
```bash
|
```bash
|
||||||
reme start config=/path/to/custom.yaml
|
reme start config=/path/to/custom.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Continue with the [CLI Reference](./reference/cli.md), [Job API Reference](./reference/jobs.md), or [Diagnostics, Backup, and Recovery](./operations.md).
|
||||||
|
|
|
||||||
87
docs/en/reference/cli.md
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
---
|
||||||
|
title: CLI Reference
|
||||||
|
description: ReMe command syntax, service invocation, configuration overrides, and plugin commands.
|
||||||
|
---
|
||||||
|
|
||||||
|
# CLI Reference
|
||||||
|
|
||||||
|
The basic syntax is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reme ACTION key=value ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Start an Application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start
|
||||||
|
reme start config=demo
|
||||||
|
reme start workspace_dir=/data/reme service.port=8181
|
||||||
|
reme start job=search query="keywords" limit=5
|
||||||
|
```
|
||||||
|
|
||||||
|
`start job=<name>` runs one Job through a one-shot service; plain `start` runs the configured Service.
|
||||||
|
|
||||||
|
## Call Jobs
|
||||||
|
|
||||||
|
Once a service is running, each action name is a Job name:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme help
|
||||||
|
reme health_check
|
||||||
|
reme search query="project decision" limit=10
|
||||||
|
reme read path=digest/wiki/project.md start_line=1 end_line=80
|
||||||
|
```
|
||||||
|
|
||||||
|
Use JSON for structured values:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme auto_memory \
|
||||||
|
session_id=example \
|
||||||
|
messages='[{"role":"user","content":"Remember this preference"}]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Client-selection arguments—`backend`, `transport`, `host`, `port`, `timeout`, `command`, `args`, and `show_metadata`—configure the client and never leak into the Job payload.
|
||||||
|
|
||||||
|
## Configuration overrides
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start \
|
||||||
|
config=/path/to/custom.yaml \
|
||||||
|
service.port=8181 \
|
||||||
|
service.web_enabled=false \
|
||||||
|
plugins='["auto-fin"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Leading `-` or `--` is optional. Use dots for nested keys and JSON for arrays and objects.
|
||||||
|
|
||||||
|
## Service discovery
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme find_reme
|
||||||
|
```
|
||||||
|
|
||||||
|
This reports a discovered service but never starts or replaces a process.
|
||||||
|
|
||||||
|
## Plugin commands
|
||||||
|
|
||||||
|
Package management runs locally rather than through HTTP or MCP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme plugins list
|
||||||
|
reme plugins show auto-fin
|
||||||
|
reme plugins validate auto-fin
|
||||||
|
reme plugins install plugins/auto-fin
|
||||||
|
reme plugins uninstall auto-fin
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Plugin Management](../plugin_management.md) for the complete workflow.
|
||||||
|
|
||||||
|
## Discover active capabilities
|
||||||
|
|
||||||
|
The [Job API Reference](./jobs.md) describes the default configuration. Plugins and custom YAML may change the running service, so automation should prefer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme help
|
||||||
|
reme app_config
|
||||||
|
```
|
||||||
|
|
@ -113,13 +113,13 @@ It is like having a recorder who is always present—not one that mechanically t
|
||||||
|
|
||||||
Not all valuable information comes from conversations. Research materials, project documents, meeting notes, archived web pages, and structured data may all become part of a personal knowledge base.
|
Not all valuable information comes from conversations. Research materials, project documents, meeting notes, archived web pages, and structured data may all become part of a personal knowledge base.
|
||||||
|
|
||||||
Auto Resource provides a general entry point for external materials. After a resource enters `resource/`, ReMe preserves the original and organizes its topics, key facts, and actionable information into daily cards with `source_resource` links. It currently supports text-based resources including Markdown, plain text, JSON, JSONL, CSV, YAML, and HTML.
|
Auto Resource provides a general entry point for external materials. After a resource enters `resource/`, ReMe preserves the original and organizes its topics, key facts, and actionable information into daily cards with `source_resource` links. It supports text resources including Markdown, plain text, JSON, JSONL, CSV, YAML, and HTML, plus image resources that a vision model turns into caption cards.
|
||||||
|
|
||||||
In other words, Auto Memory builds personal knowledge from conversations, while Auto Resource builds it from non-conversational materials. Both streams flow into the same daily memory layer, where ReMe indexes, consolidates, and retrieves them together.
|
In other words, Auto Memory builds personal knowledge from conversations, while Auto Resource builds it from non-conversational materials. Both streams flow into the same daily memory layer, where ReMe indexes, consolidates, and retrieves them together.
|
||||||
|
|
||||||
### Daily Paper: An Example External-Resource Workflow
|
### Daily Paper: An Example External-Resource Workflow
|
||||||
|
|
||||||
Daily Paper is an optional cookbook built on this file-based memory system. It collects papers from the weekly and monthly Hugging Face Papers rankings, removes items recommended recently, ranks the remaining papers, selects three, saves their PDFs, and generates Chinese paper notes and a briefing that takes about five minutes to read.
|
Daily Paper is an optional plugin built on this file-based memory system. It collects papers from the weekly and monthly Hugging Face Papers rankings, removes items recommended recently, ranks the remaining papers, selects three, saves their PDFs, and generates Chinese paper notes and a briefing that takes about five minutes to read.
|
||||||
|
|
||||||
Imagine that you regularly follow research on agent memory. Each morning, instead of receiving only three links, you get three detailed notes already saved locally. The briefing points to the original notes through Wikilinks, and each note links back to its PDF. A month later, when you ask, “What recent methods compress long-term memory?”, those materials are already in the same retrieval system. There is no need to search through browser history again.
|
Imagine that you regularly follow research on agent memory. Each morning, instead of receiving only three links, you get three detailed notes already saved locally. The briefing points to the original notes through Wikilinks, and each note links back to its PDF. A month later, when you ask, “What recent methods compress long-term memory?”, those materials are already in the same retrieval system. There is no need to search through browser history again.
|
||||||
|
|
||||||
|
|
@ -178,8 +178,9 @@ Knowledge evolves and links are created in the same workflow. Relationships are
|
||||||
|
|
||||||
Markdown is easy for people to read, but if files are merely piled into directories, agents still struggle to find them
|
Markdown is easy for people to read, but if files are merely piled into directories, agents still struggle to find them
|
||||||
quickly. The default live index watches Markdown under `daily/` and `digest/`. A separate resource workflow watches
|
quickly. The default live index watches Markdown under `daily/` and `digest/`. A separate resource workflow watches
|
||||||
`resource/` and turns those files into daily cards that enter the same index. For a full rebuild from existing files,
|
`resource/` and turns those files into daily cards that enter the same index. Manual `reindex` rebuilds BM25 and
|
||||||
`reme reindex` also scans `resource/` and JSONL.
|
embedding indexes from the chunks those ingestion paths have already accepted; it does not rescan files or rebuild the
|
||||||
|
Wikilink graph.
|
||||||
|
|
||||||
A Markdown file is parsed into:
|
A Markdown file is parsed into:
|
||||||
|
|
||||||
|
|
@ -315,10 +316,12 @@ that best fits their runtime environment and share the same local memory workspa
|
||||||
|
|
||||||
| Agent | Recommended integration | Capabilities after integration |
|
| Agent | Recommended integration | Capabilities after integration |
|
||||||
|-------|-------------------------|--------------------------------|
|
|-------|-------------------------|--------------------------------|
|
||||||
|
| **DeepSeek Harness** | Install [`@agentscope-ai/reme-dsh-plugin`](https://reme.agentscope.io/en/integrations/dsh) as a DSH profile bundle. | Long-term memory guidance, `reme_search`, automatic capture of completed main-agent turns, scheduled Auto Dream, and ReMe Status. |
|
||||||
|
| **OpenClaw** | Install [`@agentscope-ai/reme-openclaw-plugin`](https://reme.agentscope.io/en/integrations/openclaw) as the native memory plugin. | Recall before conversational root-agent runs, explicit search, automatic turn capture, scheduled Auto Dream, and status diagnostics. |
|
||||||
| **QwenPaw** | Embed ReMe in-process through the Python API. | Reuse the host application's lifecycle and model configuration while keeping memories local and file-based. |
|
| **QwenPaw** | Embed ReMe in-process through the Python API. | Reuse the host application's lifecycle and model configuration while keeping memories local and file-based. |
|
||||||
| **Claude Code** | Start the streamable HTTP MCP Service and install [`plugins/claude_code/reme`](../../plugins/claude_code/reme). | MCP memory-recall tools, the `reme-memory` skill, and a Stop hook that automatically records sessions. |
|
| **Claude Code** | Start the shared streamable HTTP MCP Service and install the [Claude Code plugin](https://reme.agentscope.io/en/integrations/claude-code). | Semantic, graph, and state recall through MCP, plus asynchronous session capture through a Stop hook. |
|
||||||
| **Hermes** | Start the HTTP Service and install [`plugins/hermes_agent`](../../plugins/hermes_agent). | Automatically recall relevant memories before model calls and invoke `auto_memory` asynchronously after each conversation turn. |
|
| **Hermes** | Install [`integrations/hermes_agent`](../../integrations/hermes_agent) and choose HTTP or embedded mode. | Automatically recall relevant memories before model calls and invoke `auto_memory` asynchronously after each conversation turn. |
|
||||||
| **OpenClaw, Codex, and other CLI-capable agents** | Copy or install [`skills/reme_memory/SKILL.md`](../../skills/reme_memory/SKILL.md). | Search, read, and write memories through the CLI; automatic recording requires the host agent to integrate explicitly with the conversation lifecycle. |
|
| **Codex and other CLI-capable agents** | Copy or install [`skills/reme_memory/SKILL.md`](../../skills/reme_memory/SKILL.md). | Search, read, and write memories through the CLI; automatic recording requires the host agent to integrate explicitly with the conversation lifecycle. |
|
||||||
|
|
||||||
For installation, configuration, and integration demos, see the [README](../../README.md).
|
For installation, configuration, and integration demos, see the [README](../../README.md).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ Conversations / external resources
|
||||||
|
|
|
|
||||||
+--> auto_dream
|
+--> auto_dream
|
||||||
| distill daily/ into digest/{personal,procedure,wiki}/
|
| distill daily/ into digest/{personal,procedure,wiki}/
|
||||||
| and write daily/<date>/interests.yaml
|
|
||||||
|
|
|
|
||||||
+--> search / node_search / read / traverse / proactive
|
+--> proactive_refresh_cron
|
||||||
|
| write daily/<date>/interests.yaml
|
||||||
|
|
|
||||||
|
+--> search / node_search / read / traverse / proactive_read
|
||||||
let agents retrieve, associate, read, and inspect interest topics
|
let agents retrieve, associate, read, and inspect interest topics
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -58,7 +60,7 @@ daily/
|
||||||
├── glencore-output-update.md
|
├── glencore-output-update.md
|
||||||
├── drc-cobalt-policy.md
|
├── drc-cobalt-policy.md
|
||||||
├── high-nickel-cathode-trend.md
|
├── high-nickel-cathode-trend.md
|
||||||
└── interests.yaml # generated after auto_dream
|
└── interests.yaml # generated by proactive refresh
|
||||||
```
|
```
|
||||||
|
|
||||||
The corresponding flow is:
|
The corresponding flow is:
|
||||||
|
|
@ -66,9 +68,9 @@ The corresponding flow is:
|
||||||
- `auto_memory` saves a filtered source conversation record to `session/dialog/<session_id>.jsonl`, then asks the agent to write
|
- `auto_memory` saves a filtered source conversation record to `session/dialog/<session_id>.jsonl`, then asks the agent to write
|
||||||
important facts to a topic-named `daily/<date>/<generated_name>.md`. The note keeps `session_id` and
|
important facts to a topic-named `daily/<date>/<generated_name>.md`. The note keeps `session_id` and
|
||||||
`source_conversation` in frontmatter for stable lookup and provenance.
|
`source_conversation` in frontmatter for stable lookup and provenance.
|
||||||
- `resource_watch_loop` watches text-file changes under `resource/` and triggers `auto_resource_step` to write a daily note
|
- `resource_watch_loop` watches supported text and image changes under `resource/` and triggers `auto_resource_step` to
|
||||||
with `source_resource`. The agent suggests a content-based filename, which the system sanitizes and de-duplicates; it is
|
write a daily note with `source_resource`. Text resources use the agent, while images use a vision model. The generated
|
||||||
not guaranteed to match the resource filename.
|
content-based filename is sanitized and de-duplicated; it is not guaranteed to match the resource filename.
|
||||||
- Auto Memory, Auto Resource, and Auto Dream refresh `daily/<date>.md` after writing.
|
- Auto Memory, Auto Resource, and Auto Dream refresh `daily/<date>.md` after writing.
|
||||||
|
|
||||||
### Day 1 evening: Auto Dream writes to Digest
|
### Day 1 evening: Auto Dream writes to Digest
|
||||||
|
|
@ -84,14 +86,14 @@ reme auto_dream date=2026-05-18
|
||||||
```text
|
```text
|
||||||
dream_extract_step
|
dream_extract_step
|
||||||
scan the daily window from 2026-05-17 through 2026-05-18 by default
|
scan the daily window from 2026-05-17 through 2026-05-18 by default
|
||||||
output at most 5 units plus topics from changed files
|
output at most 5 memory units from changed files
|
||||||
dream_integrate_step
|
dream_integrate_step
|
||||||
recall existing digest nodes with node_search for each unit
|
recall existing digest nodes with node_search for each unit
|
||||||
decide CREATE / CORROBORATE / REFINE / CORRECT
|
decide CREATE / CORROBORATE / REFINE / CORRECT
|
||||||
dream_topics_step
|
|
||||||
write daily/2026-05-18/interests.yaml
|
|
||||||
dream_finish_step
|
dream_finish_step
|
||||||
checkpoint successfully processed daily inputs
|
checkpoint successfully processed daily inputs
|
||||||
|
auto_tag_step
|
||||||
|
tag the entities in created or modified digest notes
|
||||||
```
|
```
|
||||||
|
|
||||||
Outputs in this scenario:
|
Outputs in this scenario:
|
||||||
|
|
@ -220,7 +222,7 @@ the CATL interview record on 2026-05-19.
|
||||||
|
|
||||||
### Proactive: Read the day's interest topics
|
### Proactive: Read the day's interest topics
|
||||||
|
|
||||||
`auto_dream` writes:
|
The independent proactive refresh flow writes:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
daily/2026-05-18/interests.yaml
|
daily/2026-05-18/interests.yaml
|
||||||
|
|
@ -229,24 +231,41 @@ daily/2026-05-18/interests.yaml
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
version: 2
|
||||||
date: 2026-05-18
|
date: 2026-05-18
|
||||||
topic_count: 3
|
generated_at: 2026-05-18T18:00:00+08:00
|
||||||
diversity_days: 7
|
push: true
|
||||||
topics:
|
topics:
|
||||||
- title: Impact of DRC mining-rights policy on cobalt supply
|
- id: 9c2aa7bd21bf
|
||||||
|
title: Impact of DRC mining-rights policy on cobalt supply
|
||||||
reason: The user repeatedly mentioned KFM and cobalt-price risk today
|
reason: The user repeatedly mentioned KFM and cobalt-price risk today
|
||||||
keywords: [cobalt, DRC, CMOC, KFM]
|
kind: follow_up
|
||||||
|
confidence: 0.7
|
||||||
|
first_seen: 2026-05-18
|
||||||
|
last_evidence_at: 2026-05-18
|
||||||
|
evidence: daily/2026-05-18/cobalt-supply-risk.md
|
||||||
paths:
|
paths:
|
||||||
- daily/2026-05-18/cobalt-supply-risk.md
|
- daily/2026-05-18/cobalt-supply-risk.md
|
||||||
|
agenda:
|
||||||
|
- topic_id: 9c2aa7bd21bf
|
||||||
|
title: Impact of DRC mining-rights policy on cobalt supply
|
||||||
|
scenario_type: resume_task
|
||||||
|
opener: Review the KFM policy update before the next cobalt-supply decision.
|
||||||
|
next_action: Compare the latest policy note with the existing supply-risk assessment.
|
||||||
|
preconditions: []
|
||||||
|
delivery: in_conversation
|
||||||
|
linked_memory: [daily/2026-05-18/cobalt-supply-risk.md]
|
||||||
|
order_reason: Recent evidence and a concrete next step.
|
||||||
|
suppressed: []
|
||||||
```
|
```
|
||||||
|
|
||||||
Call:
|
Call:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
reme proactive date=2026-05-18
|
reme proactive_read date=2026-05-18
|
||||||
```
|
```
|
||||||
|
|
||||||
The `proactive` Job returns the topics from `interests.yaml` and, optionally, the raw YAML content.
|
The `proactive_read` Job returns the topics from `interests.yaml` and, optionally, the raw YAML content.
|
||||||
|
|
||||||
### Value of this scenario
|
### Value of this scenario
|
||||||
|
|
||||||
|
|
|
||||||
118
docs/en/services.md
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
---
|
||||||
|
title: Services and Deployment
|
||||||
|
description: Run ReMe through HTTP, SSE, MCP, the CLI, and ReMe Studio while respecting its local security boundary.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Services and Deployment
|
||||||
|
|
||||||
|
ReMe can run as a local HTTP service, a standalone MCP server, or a one-shot CLI Job. By default HTTP binds to
|
||||||
|
`127.0.0.1:2333`; one process serves JSON, SSE, streamable HTTP MCP, and optional ReMe Studio.
|
||||||
|
|
||||||
|
## HTTP API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start
|
||||||
|
reme start service.host=127.0.0.1 service.port=8181
|
||||||
|
```
|
||||||
|
|
||||||
|
Regular Jobs become `POST /<job-name>`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s http://127.0.0.1:2333/search \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"query":"user preferences","limit":5}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Job arguments live at the request body's top level. A regular response follows the `Response` schema:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"success": true, "answer": "...", "metadata": {}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Unhandled Step failures become unsuccessful responses.
|
||||||
|
|
||||||
|
## Streaming and SSE
|
||||||
|
|
||||||
|
Jobs with `backend: stream` also use `POST /<job-name>`, returning `text/event-stream`. Error paths emit an error chunk and always terminate the stream. The default `chat` Job is streaming:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -N http://127.0.0.1:2333/chat \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"query":"Summarize my long-term preferences"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
MCP does not expose Stream Jobs.
|
||||||
|
|
||||||
|
## MCP
|
||||||
|
|
||||||
|
The default HTTP service mounts streamable HTTP MCP at `/mcp`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service:
|
||||||
|
backend: http
|
||||||
|
mcp_enabled: true
|
||||||
|
mcp_path: /mcp
|
||||||
|
mcp_stateless_http: false
|
||||||
|
```
|
||||||
|
|
||||||
|
For a standalone MCP service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start service.backend=mcp service.transport=stdio
|
||||||
|
reme start service.backend=mcp service.transport=sse service.port=2333
|
||||||
|
reme start service.backend=mcp service.transport=streamable-http service.port=2333
|
||||||
|
```
|
||||||
|
|
||||||
|
MCP tools come from non-stream Jobs with `enable_serve: true`. Use `service.jobs` as an allowlist. `injected_job_kwargs` adds server-managed values that callers cannot override.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service:
|
||||||
|
backend: http
|
||||||
|
jobs: [search, read, traverse, auto_memory]
|
||||||
|
injected_job_kwargs:
|
||||||
|
tenant_id: local-user
|
||||||
|
tool_error_on_failure: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## ReMe Studio
|
||||||
|
|
||||||
|
After installing `reme-ai[web]` or `reme-ai[core]`, the default HTTP origin also serves Studio:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://127.0.0.1:2333/
|
||||||
|
```
|
||||||
|
|
||||||
|
Disable it with `service.web_enabled=false` or select a build with `service.web_static_dir`. Missing static assets do not prevent the Job API from starting.
|
||||||
|
|
||||||
|
## One-shot Jobs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start job=search query="user preferences" limit=5
|
||||||
|
```
|
||||||
|
|
||||||
|
This selects the one-shot CLI Service while retaining the normal Application, Component, and Job lifecycle.
|
||||||
|
|
||||||
|
## Service discovery
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme find_reme
|
||||||
|
```
|
||||||
|
|
||||||
|
Ordinary `reme <action>` commands prefer the running service's actual backend, host, port, and transport. They fall back to local configuration only when no service is discovered.
|
||||||
|
|
||||||
|
## Security boundary
|
||||||
|
|
||||||
|
ReMe is local-first:
|
||||||
|
|
||||||
|
- the default binds to `127.0.0.1`;
|
||||||
|
- HTTP CORS allows any origin;
|
||||||
|
- Jobs may write, move, or delete files;
|
||||||
|
- the service layer has no general-purpose user authentication.
|
||||||
|
|
||||||
|
Remote access must be enabled explicitly with `reme start service.host=0.0.0.0`. Do not expose the service directly to
|
||||||
|
the public internet. Place it on a controlled network or behind an authenticated TLS reverse proxy, apply access
|
||||||
|
controls and request-size limits, and expose only necessary Jobs.
|
||||||
|
|
||||||
|
## OpenAPI
|
||||||
|
|
||||||
|
FastAPI exposes the active endpoints through `/docs`, `/redoc`, and `/openapi.json`. The Studio SPA fallback preserves these reserved paths.
|
||||||
8
docs/en/traffic.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
layout: home
|
||||||
|
markdownStyles: false
|
||||||
|
title: ReMe Traffic
|
||||||
|
description: Public traffic trends for the ReMe documentation site over the last 30 days.
|
||||||
|
---
|
||||||
|
|
||||||
|
<TrafficPage lang="en" />
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" role="img"
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" role="img"
|
||||||
aria-labelledby="title desc">
|
aria-labelledby="title desc">
|
||||||
<title id="title">ReMe auto dream and proactive flow</title>
|
<title id="title">ReMe auto dream and proactive flow</title>
|
||||||
<desc id="desc">A left-to-right flow from a recent changed-daily window to digest integration, interest topic
|
<desc id="desc">Independent Auto Dream and proactive flows consume daily notes: Auto Dream writes digest memory,
|
||||||
writing, catalog checkpointing, and proactive reads.
|
while proactive refresh writes interest topics for proactive reads.
|
||||||
</desc>
|
</desc>
|
||||||
<defs>
|
<defs>
|
||||||
<style>.bg { fill: #fffdf8; } .title { font: 700 30px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; } .subtitle { font: 14px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #556276; } .step-num { font: 700 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #ffffff; } .step-title { font: 700 18px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; } .step-subtitle { font: 13px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #556276; } .chip-title { font: 700 13px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; } .chip-text { font: 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5e6a7c; } .note { font: 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #4f5c6f; } .panel { fill: #ffffff; stroke: #1f2430; stroke-width: 2.2; rx: 18; ry: 18; stroke-linecap: round; stroke-linejoin: round; } .chip { fill: #f8fbff; stroke: #1f2430; stroke-width: 1.6; rx: 11; ry: 11; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 6 5; } .badge { fill: #44546a; } .arrow { stroke: #7f8b9d; stroke-width: 1.45; fill: none; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow); } .soft-arrow { stroke: #a3adbd; stroke-width: 1.25; stroke-dasharray: 6 6; fill: none; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow-soft); } .line { stroke: #a3adbd; stroke-width: 1.25; stroke-linecap: round; }</style>
|
<style>.bg { fill: #fffdf8; } .title { font: 700 30px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; } .subtitle { font: 14px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #556276; } .step-num { font: 700 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #ffffff; } .step-title { font: 700 18px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; } .step-subtitle { font: 13px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #556276; } .chip-title { font: 700 13px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; } .chip-text { font: 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5e6a7c; } .note { font: 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #4f5c6f; } .panel { fill: #ffffff; stroke: #1f2430; stroke-width: 2.2; rx: 18; ry: 18; stroke-linecap: round; stroke-linejoin: round; } .chip { fill: #f8fbff; stroke: #1f2430; stroke-width: 1.6; rx: 11; ry: 11; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 6 5; } .badge { fill: #44546a; } .arrow { stroke: #7f8b9d; stroke-width: 1.45; fill: none; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow); } .soft-arrow { stroke: #a3adbd; stroke-width: 1.25; stroke-dasharray: 6 6; fill: none; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow-soft); } .line { stroke: #a3adbd; stroke-width: 1.25; stroke-linecap: round; }</style>
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<rect class="bg" x="0" y="0" width="1200" height="640"/>
|
<rect class="bg" x="0" y="0" width="1200" height="640"/>
|
||||||
<text class="title" x="600" y="54" text-anchor="middle">Auto Dream and Proactive</text>
|
<text class="title" x="600" y="54" text-anchor="middle">Auto Dream and Proactive</text>
|
||||||
<text class="subtitle" x="600" y="80" text-anchor="middle">Scan a recent daily window, integrate a compact set of reusable units, then expose proactive topics.</text>
|
<text class="subtitle" x="600" y="80" text-anchor="middle">Independent flows turn daily notes into digest memory and proactive topics.</text>
|
||||||
|
|
||||||
<rect class="panel" x="38" y="132" width="196" height="300"/>
|
<rect class="panel" x="38" y="132" width="196" height="300"/>
|
||||||
<circle class="badge" cx="72" cy="170" r="15"/>
|
<circle class="badge" cx="72" cy="170" r="15"/>
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
<text class="chip-text" x="136" y="333" text-anchor="middle">changed daily</text>
|
<text class="chip-text" x="136" y="333" text-anchor="middle">changed daily</text>
|
||||||
<rect class="chip" x="66" y="360" width="140" height="44"/>
|
<rect class="chip" x="66" y="360" width="140" height="44"/>
|
||||||
<text class="chip-title" x="136" y="379" text-anchor="middle">LLM extract</text>
|
<text class="chip-title" x="136" y="379" text-anchor="middle">LLM extract</text>
|
||||||
<text class="chip-text" x="136" y="397" text-anchor="middle">≤ 5 units + topics</text>
|
<text class="chip-text" x="136" y="397" text-anchor="middle">≤ 5 memory units</text>
|
||||||
|
|
||||||
<rect class="panel" x="270" y="132" width="196" height="300"/>
|
<rect class="panel" x="270" y="132" width="196" height="300"/>
|
||||||
<circle class="badge" cx="304" cy="170" r="15"/>
|
<circle class="badge" cx="304" cy="170" r="15"/>
|
||||||
|
|
@ -52,37 +52,37 @@
|
||||||
<rect class="panel" x="502" y="132" width="196" height="300"/>
|
<rect class="panel" x="502" y="132" width="196" height="300"/>
|
||||||
<circle class="badge" cx="536" cy="170" r="15"/>
|
<circle class="badge" cx="536" cy="170" r="15"/>
|
||||||
<text class="step-num" x="536" y="174" text-anchor="middle">3</text>
|
<text class="step-num" x="536" y="174" text-anchor="middle">3</text>
|
||||||
<text class="step-title" x="564" y="176">Topics</text>
|
<text class="step-title" x="564" y="176">Finish</text>
|
||||||
<text class="step-subtitle" x="530" y="206">dream_topics_step</text>
|
<text class="step-subtitle" x="530" y="206">dream_finish_step</text>
|
||||||
<rect class="chip" x="530" y="232" width="140" height="44"/>
|
<rect class="chip" x="530" y="232" width="140" height="44"/>
|
||||||
<text class="chip-title" x="600" y="251" text-anchor="middle">merge topics</text>
|
<text class="chip-title" x="600" y="251" text-anchor="middle">checkpoint</text>
|
||||||
<text class="chip-text" x="600" y="269" text-anchor="middle">same day kept</text>
|
<text class="chip-text" x="600" y="269" text-anchor="middle">skip failures</text>
|
||||||
<rect class="chip" x="530" y="296" width="140" height="44"/>
|
<rect class="chip" x="530" y="296" width="140" height="44"/>
|
||||||
<text class="chip-title" x="600" y="315" text-anchor="middle">avoid repeats</text>
|
<text class="chip-title" x="600" y="315" text-anchor="middle">persist catalog</text>
|
||||||
<text class="chip-text" x="600" y="333" text-anchor="middle">last 7 days</text>
|
<text class="chip-text" x="600" y="333" text-anchor="middle">dream catalog</text>
|
||||||
<rect class="chip" x="530" y="360" width="140" height="44"/>
|
<rect class="chip" x="530" y="360" width="140" height="44"/>
|
||||||
<text class="chip-title" x="600" y="379" text-anchor="middle">write YAML</text>
|
<text class="chip-title" x="600" y="379" text-anchor="middle">return summary</text>
|
||||||
<text class="chip-text" x="600" y="397" text-anchor="middle">interests.yaml</text>
|
<text class="chip-text" x="600" y="397" text-anchor="middle">counts + errors</text>
|
||||||
|
|
||||||
<rect class="panel" x="734" y="132" width="196" height="300"/>
|
<rect class="panel" x="734" y="132" width="196" height="300"/>
|
||||||
<circle class="badge" cx="768" cy="170" r="15"/>
|
<circle class="badge" cx="768" cy="170" r="15"/>
|
||||||
<text class="step-num" x="768" y="174" text-anchor="middle">4</text>
|
<text class="step-num" x="768" y="174" text-anchor="middle">P</text>
|
||||||
<text class="step-title" x="796" y="176">Finish</text>
|
<text class="step-title" x="796" y="176">Refresh</text>
|
||||||
<text class="step-subtitle" x="762" y="206">dream_finish_step</text>
|
<text class="step-subtitle" x="762" y="206">proactive_refresh_cron</text>
|
||||||
<rect class="chip" x="762" y="232" width="140" height="44"/>
|
<rect class="chip" x="762" y="232" width="140" height="44"/>
|
||||||
<text class="chip-title" x="832" y="251" text-anchor="middle">checkpoint</text>
|
<text class="chip-title" x="832" y="251" text-anchor="middle">extract topics</text>
|
||||||
<text class="chip-text" x="832" y="269" text-anchor="middle">skip failures</text>
|
<text class="chip-text" x="832" y="269" text-anchor="middle">changed daily</text>
|
||||||
<rect class="chip" x="762" y="296" width="140" height="44"/>
|
<rect class="chip" x="762" y="296" width="140" height="44"/>
|
||||||
<text class="chip-title" x="832" y="315" text-anchor="middle">persist catalog</text>
|
<text class="chip-title" x="832" y="315" text-anchor="middle">plan agenda</text>
|
||||||
<text class="chip-text" x="832" y="333" text-anchor="middle">file_catalog</text>
|
<text class="chip-text" x="832" y="333" text-anchor="middle">filter + order</text>
|
||||||
<rect class="chip" x="762" y="360" width="140" height="44"/>
|
<rect class="chip" x="762" y="360" width="140" height="44"/>
|
||||||
<text class="chip-title" x="832" y="379" text-anchor="middle">return summary</text>
|
<text class="chip-title" x="832" y="379" text-anchor="middle">write YAML</text>
|
||||||
<text class="chip-text" x="832" y="397" text-anchor="middle">counts + errors</text>
|
<text class="chip-text" x="832" y="397" text-anchor="middle">interests.yaml</text>
|
||||||
|
|
||||||
<rect class="panel" x="966" y="132" width="196" height="300"/>
|
<rect class="panel" x="966" y="132" width="196" height="300"/>
|
||||||
<circle class="badge" cx="1000" cy="170" r="15"/>
|
<circle class="badge" cx="1000" cy="170" r="15"/>
|
||||||
<text class="step-num" x="1000" y="174" text-anchor="middle">5</text>
|
<text class="step-num" x="1000" y="174" text-anchor="middle">R</text>
|
||||||
<text class="step-title" x="1028" y="176">Proactive</text>
|
<text class="step-title" x="1028" y="176">Read</text>
|
||||||
<text class="step-subtitle" x="994" y="206">proactive_step</text>
|
<text class="step-subtitle" x="994" y="206">proactive_step</text>
|
||||||
<rect class="chip" x="994" y="232" width="140" height="44"/>
|
<rect class="chip" x="994" y="232" width="140" height="44"/>
|
||||||
<text class="chip-title" x="1064" y="251" text-anchor="middle">read YAML</text>
|
<text class="chip-title" x="1064" y="251" text-anchor="middle">read YAML</text>
|
||||||
|
|
@ -96,7 +96,6 @@
|
||||||
|
|
||||||
<path class="arrow" d="M234 282 H270"/>
|
<path class="arrow" d="M234 282 H270"/>
|
||||||
<path class="arrow" d="M466 282 H502"/>
|
<path class="arrow" d="M466 282 H502"/>
|
||||||
<path class="arrow" d="M698 282 H734"/>
|
|
||||||
<path class="arrow" d="M930 282 H966"/>
|
<path class="arrow" d="M930 282 H966"/>
|
||||||
|
|
||||||
<rect class="panel" x="80" y="502" width="1040" height="82"/>
|
<rect class="panel" x="80" y="502" width="1040" height="82"/>
|
||||||
|
|
@ -112,6 +111,5 @@
|
||||||
<text class="chip-title" x="972" y="532">Boundary</text>
|
<text class="chip-title" x="972" y="532">Boundary</text>
|
||||||
<text class="chip-text" x="972" y="554" style="font-size:11px">Read-only; caller decides</text>
|
<text class="chip-text" x="972" y="554" style="font-size:11px">Read-only; caller decides</text>
|
||||||
|
|
||||||
<path class="soft-arrow" d="M1064 432 C1064 476 600 472 600 432"/>
|
<text class="note" x="834" y="474" text-anchor="middle">Auto Dream and proactive refresh are independent consumers of daily notes</text>
|
||||||
<text class="note" x="834" y="474" text-anchor="middle">proactive reads interests.yaml after auto_dream writes it</text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.1 KiB |
|
|
@ -73,7 +73,7 @@
|
||||||
|
|
||||||
<rect class="panel" x="142" y="484" width="916" height="76"/>
|
<rect class="panel" x="142" y="484" width="916" height="76"/>
|
||||||
<text class="note" x="190" y="514">Rebuild scope</text>
|
<text class="note" x="190" y="514">Rebuild scope</text>
|
||||||
<text class="chip-text" x="190" y="536">reindex adds resource + JSONL.</text>
|
<text class="chip-text" x="190" y="536">BM25 + vectors from current chunks.</text>
|
||||||
<line class="line" x1="410" y1="500" x2="410" y2="544"/>
|
<line class="line" x1="410" y1="500" x2="410" y2="544"/>
|
||||||
<text class="chip-title" x="454" y="514">BM25 is enabled by default</text>
|
<text class="chip-title" x="454" y="514">BM25 is enabled by default</text>
|
||||||
<text class="chip-text" x="454" y="536">Recall cap: 200; embeddings opt-in.</text>
|
<text class="chip-text" x="454" y="536">Recall cap: 200; embeddings opt-in.</text>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
|
|
@ -1,8 +1,8 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" role="img"
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" role="img"
|
||||||
aria-labelledby="title desc">
|
aria-labelledby="title desc">
|
||||||
<title id="title">Memory Index</title>
|
<title id="title">Memory Index</title>
|
||||||
<desc id="desc">Watch daily and digest Markdown, rebuild broader workspace content on demand, split files into
|
<desc id="desc">Watch daily and digest Markdown, transform resources through the resource workflow, split files
|
||||||
structural semantic chunks, and build BM25, optional vector, and Wikilink graph indexes.
|
into structural semantic chunks, and build BM25, optional vector, and Wikilink graph indexes.
|
||||||
</desc>
|
</desc>
|
||||||
<defs>
|
<defs>
|
||||||
<style>.bg{fill:#fffdf8}.title{font:800 30px Arial,sans-serif;fill:#1f2430}.sub{font:14px Arial,sans-serif;fill:#667085}.panel{fill:#fff;stroke:#1f2430;stroke-width:2;rx:18}.head{font:700 17px Arial,sans-serif;fill:#1f2430}.text{font:13px Arial,sans-serif;fill:#5e6a7c}.tiny{font:12px Arial,sans-serif;fill:#667085}.mono{font:700 13px ui-monospace,SFMono-Regular,Menlo,monospace;fill:#1f2430}.chip{fill:#f8fbff;stroke:#1f2430;stroke-width:1.4;stroke-dasharray:6 5;rx:10}.orange{fill:#fff2e5}.blue{fill:#eef7ff}.green{fill:#effaf5}.arrow{fill:none;stroke:#7f8b9d;stroke-width:2;marker-end:url(#a)}.loop{fill:none;stroke:#ff963d;stroke-width:2;stroke-dasharray:7 6;marker-end:url(#o)}</style>
|
<style>.bg{fill:#fffdf8}.title{font:800 30px Arial,sans-serif;fill:#1f2430}.sub{font:14px Arial,sans-serif;fill:#667085}.panel{fill:#fff;stroke:#1f2430;stroke-width:2;rx:18}.head{font:700 17px Arial,sans-serif;fill:#1f2430}.text{font:13px Arial,sans-serif;fill:#5e6a7c}.tiny{font:12px Arial,sans-serif;fill:#667085}.mono{font:700 13px ui-monospace,SFMono-Regular,Menlo,monospace;fill:#1f2430}.chip{fill:#f8fbff;stroke:#1f2430;stroke-width:1.4;stroke-dasharray:6 5;rx:10}.orange{fill:#fff2e5}.blue{fill:#eef7ff}.green{fill:#effaf5}.arrow{fill:none;stroke:#7f8b9d;stroke-width:2;marker-end:url(#a)}.loop{fill:none;stroke:#ff963d;stroke-width:2;stroke-dasharray:7 6;marker-end:url(#o)}</style>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<text class="mono" x="161" y="327" text-anchor="middle">digest/</text>
|
<text class="mono" x="161" y="327" text-anchor="middle">digest/</text>
|
||||||
<rect class="chip green" x="72" y="374" width="178" height="58"/>
|
<rect class="chip green" x="72" y="374" width="178" height="58"/>
|
||||||
<text class="mono" x="161" y="401" text-anchor="middle">resource/</text>
|
<text class="mono" x="161" y="401" text-anchor="middle">resource/</text>
|
||||||
<text class="tiny" x="161" y="421" text-anchor="middle">via reindex</text>
|
<text class="tiny" x="161" y="421" text-anchor="middle">via resource workflow</text>
|
||||||
<text class="tiny" x="161" y="464" text-anchor="middle">Create · update · delete</text>
|
<text class="tiny" x="161" y="464" text-anchor="middle">Create · update · delete</text>
|
||||||
|
|
||||||
<path class="arrow" d="M280 312h58"/>
|
<path class="arrow" d="M280 312h58"/>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
73
docs/figure/reme-blog/reme-blog-memory-tags.svg
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="620" viewBox="0 0 1200 620" role="img"
|
||||||
|
aria-labelledby="title desc">
|
||||||
|
<title id="title">ReMe Memory Tags Workflow</title>
|
||||||
|
<desc id="desc">Markdown memories use the memory_tags frontmatter field to build a rebuildable bidirectional tag index. Search first narrows the file scope by tag, then applies keyword or semantic retrieval.</desc>
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.bg{fill:#fffdf8}.title{font:800 30px Arial,"PingFang SC",sans-serif;fill:#1f2430}
|
||||||
|
.sub{font:15px Arial,"PingFang SC",sans-serif;fill:#667085}.panel{fill:#fff;stroke:#1f2430;stroke-width:2}
|
||||||
|
.head{font:700 18px Arial,"PingFang SC",sans-serif;fill:#1f2430}.text{font:14px Arial,"PingFang SC",sans-serif;fill:#5e6a7c}
|
||||||
|
.tiny{font:12px Arial,"PingFang SC",sans-serif;fill:#667085}.mono{font:700 14px ui-monospace,SFMono-Regular,Menlo,monospace;fill:#1f2430}
|
||||||
|
.orange{fill:#fff2e5}.blue{fill:#eef7ff}.green{fill:#effaf5}.purple{fill:#f5f1ff}
|
||||||
|
.chip{stroke:#1f2430;stroke-width:1.4}.arrow{fill:none;stroke:#7f8b9d;stroke-width:2.2;marker-end:url(#arrow)}
|
||||||
|
.dash{fill:none;stroke:#ff963d;stroke-width:2;stroke-dasharray:7 6;marker-end:url(#orange-arrow)}
|
||||||
|
</style>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
|
||||||
|
<path d="M0 0v8l8-4z" fill="#7f8b9d"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="orange-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
|
||||||
|
<path d="M0 0v8l8-4z" fill="#ff963d"/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect class="bg" width="1200" height="620"/>
|
||||||
|
<text class="title" x="600" y="48" text-anchor="middle">From Markdown Tags to More Precise Memory Recall</text>
|
||||||
|
<text class="sub" x="600" y="76" text-anchor="middle">Files remain the source of truth; the tag index is a rebuildable map of memory cues</text>
|
||||||
|
|
||||||
|
<rect class="panel" x="40" y="120" width="270" height="350" rx="18"/>
|
||||||
|
<text class="head" x="175" y="158" text-anchor="middle">1. A Readable Memory</text>
|
||||||
|
<path d="M92 195h130l34 34v170H92z" fill="#fff7e8" stroke="#1f2430" stroke-width="2"/>
|
||||||
|
<path d="M222 195v34h34" fill="none" stroke="#1f2430" stroke-width="2"/>
|
||||||
|
<text class="mono" x="112" y="255">---</text>
|
||||||
|
<text class="mono" x="112" y="282">memory_tags:</text>
|
||||||
|
<text class="mono" x="112" y="309"> - Alice</text>
|
||||||
|
<text class="mono" x="112" y="336"> - Project_A</text>
|
||||||
|
<text class="mono" x="112" y="363">---</text>
|
||||||
|
<text class="tiny" x="175" y="432" text-anchor="middle">The body stays plain Markdown</text>
|
||||||
|
|
||||||
|
<path class="arrow" d="M310 295h70"/>
|
||||||
|
|
||||||
|
<rect class="panel" x="380" y="120" width="390" height="350" rx="18"/>
|
||||||
|
<text class="head" x="575" y="158" text-anchor="middle">2. Build a Bidirectional Tag Index</text>
|
||||||
|
<rect class="chip blue" x="420" y="195" width="140" height="70" rx="12"/>
|
||||||
|
<text class="head" x="490" y="226" text-anchor="middle">Alice</text>
|
||||||
|
<text class="tiny" x="490" y="247" text-anchor="middle">tag → files</text>
|
||||||
|
<rect class="chip green" x="590" y="195" width="140" height="70" rx="12"/>
|
||||||
|
<text class="head" x="660" y="226" text-anchor="middle">Project_A</text>
|
||||||
|
<text class="tiny" x="660" y="247" text-anchor="middle">tag → files</text>
|
||||||
|
<rect class="chip purple" x="455" y="320" width="240" height="78" rx="12"/>
|
||||||
|
<text class="mono" x="575" y="352" text-anchor="middle">daily/decision.md</text>
|
||||||
|
<text class="tiny" x="575" y="376" text-anchor="middle">file → tags</text>
|
||||||
|
<path class="dash" d="M490 265v42h85"/>
|
||||||
|
<path class="dash" d="M660 265v42h-85"/>
|
||||||
|
<text class="tiny" x="575" y="439" text-anchor="middle">Updated on create, edit, and delete</text>
|
||||||
|
|
||||||
|
<path class="arrow" d="M770 295h70"/>
|
||||||
|
|
||||||
|
<rect class="panel" x="840" y="120" width="320" height="350" rx="18"/>
|
||||||
|
<text class="head" x="1000" y="158" text-anchor="middle">3. Search with a Memory Cue</text>
|
||||||
|
<rect class="chip orange" x="875" y="190" width="250" height="88" rx="12"/>
|
||||||
|
<text class="tiny" x="1000" y="214" text-anchor="middle">query</text>
|
||||||
|
<text class="text" x="1000" y="237" text-anchor="middle">What must we check before launch?</text>
|
||||||
|
<text class="mono" x="1000" y="260" text-anchor="middle">tags: [Project_A]</text>
|
||||||
|
<path class="arrow" d="M1000 278v37"/>
|
||||||
|
<rect class="chip blue" x="875" y="315" width="250" height="88" rx="12"/>
|
||||||
|
<text class="head" x="1000" y="345" text-anchor="middle">Narrow Direct</text>
|
||||||
|
<text class="head" x="1000" y="367" text-anchor="middle">Search Scope</text>
|
||||||
|
<text class="tiny" x="1000" y="389" text-anchor="middle">Direct hits must match the tag filter</text>
|
||||||
|
<path class="arrow" d="M1000 403v19"/>
|
||||||
|
<text class="text" x="1000" y="447" text-anchor="middle">BM25 + optional vector retrieval</text>
|
||||||
|
|
||||||
|
<path class="dash" d="M1000 470v88H175v-88"/>
|
||||||
|
<text class="text" x="600" y="590" text-anchor="middle">If the index is lost, source files remain intact—rebuild it from Markdown frontmatter</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
27
docs/figure/reme-icon.svg
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="ReMe">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="brand" x1="14" y1="12" x2="51" y2="51" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#21d1bb"/>
|
||||||
|
<stop offset="0.52" stop-color="#159fc9"/>
|
||||||
|
<stop offset="1" stop-color="#3156d9"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="glass" x1="10" y1="7" x2="56" y2="59" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#ffffff" stop-opacity="0.94"/>
|
||||||
|
<stop offset="0.48" stop-color="#e4fffa" stop-opacity="0.82"/>
|
||||||
|
<stop offset="1" stop-color="#dbe7ff" stop-opacity="0.74"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="shadow" x="-30%" y="-30%" width="160%" height="170%">
|
||||||
|
<feDropShadow dx="0" dy="3" stdDeviation="3" flood-color="#287ca6" flood-opacity="0.22"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<g filter="url(#shadow)">
|
||||||
|
<rect x="3" y="3" width="58" height="58" rx="18" fill="url(#glass)"/>
|
||||||
|
<rect x="3.75" y="3.75" width="56.5" height="56.5" rx="17.25" fill="none" stroke="url(#brand)" stroke-opacity="0.46" stroke-width="1.5"/>
|
||||||
|
<path d="M10 21C18 8 43 7 55 17" fill="none" stroke="white" stroke-width="2.2" stroke-linecap="round" opacity="0.9"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<path d="M18 49V15h14.5C41 15 46 19.5 46 27s-5 12-13.5 12H27" fill="none" stroke="url(#brand)" stroke-width="7.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M27 38.5 46 49" fill="none" stroke="url(#brand)" stroke-width="7.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="27" cy="38.5" r="2.2" fill="white" stroke="#43d7c5" stroke-width="0.8"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
46
docs/figure/reme-logo-fashion.svg
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 780 220" role="img" aria-labelledby="title description">
|
||||||
|
<title id="title">ReMe</title>
|
||||||
|
<desc id="description">ReMe gradient wordmark and open memory ribbon</desc>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="brand" x1="24" y1="28" x2="746" y2="188" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#20d2b7"/>
|
||||||
|
<stop offset="0.4" stop-color="#12a9cc"/>
|
||||||
|
<stop offset="0.74" stop-color="#2877e6"/>
|
||||||
|
<stop offset="1" stop-color="#5146e5"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="icon-glass" x1="22" y1="18" x2="194" y2="204" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#ffffff" stop-opacity="0.96"/>
|
||||||
|
<stop offset="0.5" stop-color="#e8fffb" stop-opacity="0.84"/>
|
||||||
|
<stop offset="1" stop-color="#dce8ff" stop-opacity="0.76"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="wordmark" x1="252" y1="72" x2="742" y2="164" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#12b6bd"/>
|
||||||
|
<stop offset="0.5" stop-color="#168fd0"/>
|
||||||
|
<stop offset="1" stop-color="#4961dc"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="soft-shadow" x="-25%" y="-30%" width="150%" height="170%">
|
||||||
|
<feDropShadow dx="0" dy="8" stdDeviation="8" flood-color="#237caa" flood-opacity="0.16"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="icon-shadow" x="-25%" y="-25%" width="150%" height="160%">
|
||||||
|
<feDropShadow dx="0" dy="7" stdDeviation="8" flood-color="#287ca6" flood-opacity="0.2"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="wordmark-shadow" x="-10%" y="-20%" width="120%" height="150%">
|
||||||
|
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#2876b8" flood-opacity="0.18"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- The memory ribbon lives in its own glass app tile, separated from the wordmark. -->
|
||||||
|
<g filter="url(#icon-shadow)">
|
||||||
|
<rect x="15" y="15" width="190" height="190" rx="54" fill="url(#icon-glass)"/>
|
||||||
|
<rect x="16.5" y="16.5" width="187" height="187" rx="52.5" fill="none" stroke="url(#brand)" stroke-opacity="0.48" stroke-width="3"/>
|
||||||
|
<path d="M37 69C63 27 144 24 182 57" fill="none" stroke="white" stroke-width="7" stroke-linecap="round" opacity="0.92"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g fill="none" stroke="url(#brand)" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M64 166V54h48c28 0 45 15 45 39.5S140 133 112 133H94" stroke-width="25"/>
|
||||||
|
<path d="M94 131 157 166" stroke-width="25"/>
|
||||||
|
</g>
|
||||||
|
<circle cx="94" cy="131" r="7" fill="white" stroke="#3bd7c2" stroke-width="3"/>
|
||||||
|
|
||||||
|
<text x="252" y="159" fill="url(#wordmark)" font-family="Optima, Candara, 'Segoe UI', sans-serif" font-size="132" font-weight="600" letter-spacing="-3" filter="url(#wordmark-shadow)">ReMe</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
43
docs/index.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
layout: home
|
||||||
|
title: ReMe Documentation
|
||||||
|
titleTemplate: false
|
||||||
|
head:
|
||||||
|
- - meta
|
||||||
|
- http-equiv: refresh
|
||||||
|
content: "0; url=/zh/"
|
||||||
|
- - link
|
||||||
|
- rel: canonical
|
||||||
|
href: https://reme.agentscope.io/zh/
|
||||||
|
hero:
|
||||||
|
name: ReMe
|
||||||
|
text: 让 Agent 真正记住
|
||||||
|
tagline: 文件属于你,记忆服务于 Agent。ReMe 将对话和资料沉淀为可读、可编辑、可检索、相互链接的本地文件。
|
||||||
|
image:
|
||||||
|
src: /reme-logo.svg
|
||||||
|
alt: ReMe Logo
|
||||||
|
actions:
|
||||||
|
- theme: brand
|
||||||
|
text: 快速开始
|
||||||
|
link: /zh/quick_start
|
||||||
|
- theme: alt
|
||||||
|
text: 核心概念
|
||||||
|
link: /zh/memory_as_file
|
||||||
|
features:
|
||||||
|
- icon: 📁
|
||||||
|
title: Local-first
|
||||||
|
details: Workspace 文件是持久事实源;索引、图谱和缓存都可以重新构建。
|
||||||
|
link: /zh/memory_as_file
|
||||||
|
- icon: 🧠
|
||||||
|
title: Memory workflows
|
||||||
|
details: 将对话和资料写入 daily,自动整理为互相关联的长期 digest。
|
||||||
|
link: /zh/auto_memory
|
||||||
|
- icon: 🔎
|
||||||
|
title: Search and graph
|
||||||
|
details: 结合关键词、可选向量检索与 wikilink 图谱,渐进式展开上下文。
|
||||||
|
link: /zh/memory_search
|
||||||
|
- icon: 🔌
|
||||||
|
title: Agent integrations
|
||||||
|
details: 通过 CLI、HTTP、MCP 和宿主适配器接入已有 Agent。
|
||||||
|
link: /zh/integrations
|
||||||
|
---
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
# Auto Dream
|
# Auto Dream
|
||||||
|
|
||||||
`auto_dream` 是 ReMe 的 daily 到 digest 的长期记忆沉淀流程。它默认扫描目标日期及前一天的 daily 输入,只处理相对上次 dream
|
`auto_dream` 是 ReMe 的 daily 到 digest 的长期记忆沉淀流程。它默认扫描目标日期及前一天的 daily 输入,只处理相对上次 dream
|
||||||
发生变化的文件,从整个扫描窗口中抽取少量高价值 memory units,整合进 `digest/`,再生成目标日期可供主动提醒使用的
|
发生变化的文件,从整个扫描窗口中抽取少量高价值 memory units,并整合进 `digest/`。
|
||||||
`interests.yaml`。
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img src="../figure/auto-dream-and-proactive.svg" alt="ReMe Auto Dream and Proactive 从 daily 到 digest 再到 proactive 的流程" width="92%">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
它消费的 daily 输入通常来自 [Auto Memory](./auto_memory.md) 和 [Auto Resource](./auto_resource.md)。`digest/`、Sources 章节
|
它消费的 daily 输入通常来自 [Auto Memory](./auto_memory.md) 和 [Auto Resource](./auto_resource.md)。`digest/`、Sources 章节
|
||||||
和 wikilink 的文件语义见 [Memory as File](./memory_as_file.md);Integrate 阶段的链接策略详见 [Auto Link](./auto_link.md)。
|
和 wikilink 的文件语义见 [Memory as File](./memory_as_file.md);Integrate 阶段的链接策略详见 [Auto Link](./auto_link.md)。
|
||||||
`interests.yaml` 的读取接口见 [Proactive](./proactive.md)。
|
主动发现是独立流程,见 [Proactive](./proactive.md)。
|
||||||
|
|
||||||
## 配置入口
|
## 配置入口
|
||||||
|
|
||||||
|
|
@ -32,24 +27,15 @@ auto_dream:
|
||||||
max_units:
|
max_units:
|
||||||
type: integer
|
type: integer
|
||||||
default: 5
|
default: 5
|
||||||
topic_count:
|
|
||||||
type: integer
|
|
||||||
default: 3
|
|
||||||
topic_diversity_days:
|
|
||||||
type: integer
|
|
||||||
default: 7
|
|
||||||
steps:
|
steps:
|
||||||
- backend: dream_extract_step
|
- backend: dream_extract_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
topic_session_id: interests
|
|
||||||
scan_days: 2
|
scan_days: 2
|
||||||
max_units: 5
|
max_units: 5
|
||||||
- backend: dream_integrate_step
|
- backend: dream_integrate_step
|
||||||
- backend: dream_topics_step
|
|
||||||
topic_count: 3
|
|
||||||
topic_diversity_days: 7
|
|
||||||
- backend: dream_finish_step
|
- backend: dream_finish_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
|
- backend: auto_tag_step
|
||||||
```
|
```
|
||||||
|
|
||||||
参数含义:
|
参数含义:
|
||||||
|
|
@ -60,8 +46,6 @@ auto_dream:
|
||||||
| `hint` | 调用方给抽取和整合阶段的额外指导。 |
|
| `hint` | 调用方给抽取和整合阶段的额外指导。 |
|
||||||
| `scan_days` | 以 `date` 结尾的最近日期窗口;默认扫描 2 天,最小为 1。 |
|
| `scan_days` | 以 `date` 结尾的最近日期窗口;默认扫描 2 天,最小为 1。 |
|
||||||
| `max_units` | 一次最多抽取多少个可复用 unit;默认 5。 |
|
| `max_units` | 一次最多抽取多少个可复用 unit;默认 5。 |
|
||||||
| `topic_count` | 最终写入 `interests.yaml` 的 topic 上限,默认 3。 |
|
|
||||||
| `topic_diversity_days` | 选择 topic 时参考过去多少天的 `interests.yaml` 避免重复,默认 7。 |
|
|
||||||
|
|
||||||
## 输入和输出
|
## 输入和输出
|
||||||
|
|
||||||
|
|
@ -74,7 +58,7 @@ daily/2026-06-20.md
|
||||||
daily/2026-06-20/**/*.md
|
daily/2026-06-20/**/*.md
|
||||||
```
|
```
|
||||||
|
|
||||||
扫描窗口内的 `daily/<date>/interests.yaml` 都不作为抽取输入,避免上一轮主动主题反过来污染下一轮抽取。最终 topic 只写入目标日期。
|
Auto Dream 只扫描 Markdown 日期索引和笔记,不读取 proactive 状态或 `interests.yaml`。
|
||||||
|
|
||||||
主要输出有三类:
|
主要输出有三类:
|
||||||
|
|
||||||
|
|
@ -83,7 +67,6 @@ daily/2026-06-20/**/*.md
|
||||||
| `digest/procedure/*.md` | 方法、流程、runbook、可执行经验。 |
|
| `digest/procedure/*.md` | 方法、流程、runbook、可执行经验。 |
|
||||||
| `digest/personal/*.md` | 用户、团队、项目相关的偏好、事实、长期上下文。 |
|
| `digest/personal/*.md` | 用户、团队、项目相关的偏好、事实、长期上下文。 |
|
||||||
| `digest/wiki/*.md` | 通用知识、概念、观察、决策先例。 |
|
| `digest/wiki/*.md` | 通用知识、概念、观察、决策先例。 |
|
||||||
| `daily/<date>/interests.yaml` | 当天值得上层 Agent 主动关注的兴趣主题。 |
|
|
||||||
| `metadata/file_catalog/dream*` | dream 专用 catalog,用于判断 daily 输入是否变化。 |
|
| `metadata/file_catalog/dream*` | dream 专用 catalog,用于判断 daily 输入是否变化。 |
|
||||||
|
|
||||||
## 四个阶段
|
## 四个阶段
|
||||||
|
|
@ -94,16 +77,14 @@ daily/2026-06-20/**/*.md
|
||||||
|
|
||||||
1. 刷新扫描窗口内每天的索引页 `daily/<date>.md`。
|
1. 刷新扫描窗口内每天的索引页 `daily/<date>.md`。
|
||||||
2. 扫描这些日期的索引页和 `daily/<date>/**/*.md`,与 `file_catalog: dream` 中记录的 mtime 对比。
|
2. 扫描这些日期的索引页和 `daily/<date>/**/*.md`,与 `file_catalog: dream` 中记录的 mtime 对比。
|
||||||
3. 只把 changed files 一起交给 LLM,全局抽取两类结构化结果:`units` 和 `topics`。
|
3. 只把 changed files 一起交给 LLM,全局抽取结构化 memory `units`。
|
||||||
|
|
||||||
`units` 是准备沉淀进 digest 的长期记忆单元,包含 `name`、`bucket`、`summary`、`paths`。一次最多返回 `max_units`
|
`units` 是准备沉淀进 digest 的长期记忆单元,包含 `name`、`bucket`、`summary`、`paths`。一次最多返回 `max_units`
|
||||||
个,抽取器会优先合并指向同一抽象的跨文件证据,并丢弃短暂提及、逐文件摘要和缺少复用价值的弱候选。`bucket` 只允许
|
个,抽取器会优先合并指向同一抽象的跨文件证据,并丢弃短暂提及、逐文件摘要和缺少复用价值的弱候选。`bucket` 只允许
|
||||||
`procedure`、`personal`、`wiki`;未知值会路由到 `wiki`。
|
`procedure`、`personal`、`wiki`;未知值会路由到 `wiki`。
|
||||||
|
|
||||||
`topics` 是当天主动兴趣候选,包含 `title`、`reason`、`evidence`、`keywords`、`paths`,后续由 Topics 阶段再筛选。
|
如果没有 changed files,Extract 会成功返回空 units;Integrate 随后没有 unit 可处理,Finish 仍会正常汇总 catalog。
|
||||||
|
如果有变化但没有配置 LLM,Extract 会失败,因为抽取依赖 LLM。
|
||||||
如果没有 changed files,Extract 会成功返回空 units;Integrate 随后没有 unit 可处理,Topics 保留目标日期已有的 topics,Finish
|
|
||||||
仍会正常汇总 catalog。如果有变化但没有配置 LLM,Extract 会失败,因为抽取依赖 LLM。
|
|
||||||
|
|
||||||
### 2. Integrate
|
### 2. Integrate
|
||||||
|
|
||||||
|
|
@ -131,48 +112,29 @@ digest 节点。新增与更新都必须保留来源,并把相关 digest 链
|
||||||
Integrate 成功的 unit 会记录到 `integrate_results`;失败的 unit 会进入 `failed_units`,其来源路径会进入 `failed_paths`。
|
Integrate 成功的 unit 会记录到 `integrate_results`;失败的 unit 会进入 `failed_units`,其来源路径会进入 `failed_paths`。
|
||||||
Finish 阶段不会 checkpoint 失败路径,保证下次还能重试。
|
Finish 阶段不会 checkpoint 失败路径,保证下次还能重试。
|
||||||
|
|
||||||
### 3. Topics
|
### 3. Finish
|
||||||
|
|
||||||
`dream_topics_step` 将 Extract 阶段产生的 topic candidates 变成当天最终的 `daily/<date>/interests.yaml`。
|
|
||||||
|
|
||||||
它会读取:
|
|
||||||
|
|
||||||
```text
|
|
||||||
daily/<date>/interests.yaml
|
|
||||||
daily/<过去 topic_diversity_days 天中的每一天>/interests.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
同一天已有 topics 会被保留,最近 `topic_diversity_days` 天出现过的相似主题会被去重。默认最多写 3 个 topic。配置了 LLM 时会让
|
|
||||||
LLM 选择更具体、可行动、非重复的主题;没有 LLM 时会退化成本地规范化去重。
|
|
||||||
|
|
||||||
写入格式示例。读取这个文件的接口见 [Proactive](./proactive.md):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
date: 2026-06-20
|
|
||||||
topic_count: 3
|
|
||||||
diversity_days: 7
|
|
||||||
topics:
|
|
||||||
- title: 记忆检索链路的质量回归
|
|
||||||
reason: 用户近期持续修改 search、node_search 和 dream 集成链路。
|
|
||||||
evidence: daily/2026-06-20/session.md
|
|
||||||
keywords:
|
|
||||||
- memory search
|
|
||||||
- auto dream
|
|
||||||
paths:
|
|
||||||
- daily/2026-06-20/session.md
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Finish
|
|
||||||
|
|
||||||
`dream_finish_step` 负责收尾:
|
`dream_finish_step` 负责收尾:
|
||||||
|
|
||||||
1. 将成功处理的 changed paths 写入 `file_catalog: dream`。
|
1. 将成功处理的 changed paths 写入 `file_catalog: dream`。
|
||||||
2. 将目标日期的 `daily/<date>/interests.yaml` 和扫描窗口内每个已刷新的 day-index 页也写入 catalog。
|
2. 将扫描窗口内每个已刷新的 day-index 页也写入 catalog。
|
||||||
3. 如果有 upsert 或 delete,持久化 dream catalog。
|
3. 如果有 upsert 或 delete,持久化 dream catalog。
|
||||||
4. 返回包含 scanned、changed、integrated、topics、checkpoint 等计数的摘要。
|
4. 返回包含 scanned、changed、integrated、checkpoint 等计数的摘要。
|
||||||
|
|
||||||
|
Auto Dream 不读取或写入 proactive 状态和 `interests.yaml`。这些文件由 proactive refresh writer 链路负责,
|
||||||
|
见 [Proactive](./proactive.md)。
|
||||||
|
|
||||||
失败路径不会被 checkpoint。这样下一次 `auto_dream` 仍会把它们视作 changed input,直到整合成功。
|
失败路径不会被 checkpoint。这样下一次 `auto_dream` 仍会把它们视作 changed input,直到整合成功。
|
||||||
|
|
||||||
|
### 4. Auto Tag
|
||||||
|
|
||||||
|
Finish 后,`auto_dream` 和 `dream_cron` 都会通过 `auto_tag_step` 为本轮整合实际新增或修改的 Markdown digest 文件打标,
|
||||||
|
包括 Agent 异常后恢复的落盘结果。同一文件被多次写入时只打标一次。该 Step 复用 [Auto Memory](./auto_memory.md) 的请求级
|
||||||
|
`changes` 协议,将实体标签写入配置的 frontmatter 字段,默认为 `memory_tags`。Dream 不为未变化文件或 daily 来源笔记打标。
|
||||||
|
|
||||||
|
打标诊断记录在 `metadata.auto_tag`。单文件打标失败保留 dream 原有的摘要、成功状态和 checkpoint 决策;后续没有文件变化的
|
||||||
|
调用不会自动重试失败的打标。标签索引通过现有文件 watcher 异步更新。
|
||||||
|
|
||||||
## 运行方式
|
## 运行方式
|
||||||
|
|
||||||
CLI:
|
CLI:
|
||||||
|
|
@ -204,9 +166,9 @@ jobs:
|
||||||
- backend: dream_extract_step
|
- backend: dream_extract_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
- backend: dream_integrate_step
|
- backend: dream_integrate_step
|
||||||
- backend: dream_topics_step
|
|
||||||
- backend: dream_finish_step
|
- backend: dream_finish_step
|
||||||
file_catalog: dream
|
file_catalog: dream
|
||||||
|
- backend: auto_tag_step
|
||||||
```
|
```
|
||||||
|
|
||||||
## 关键边界
|
## 关键边界
|
||||||
|
|
@ -217,7 +179,6 @@ jobs:
|
||||||
`该决策记录在 [[daily/<date>/decision.md]] 中。`链接写法遵循
|
`该决策记录在 [[daily/<date>/decision.md]] 中。`链接写法遵循
|
||||||
[Memory as File](./memory_as_file.md) 中的 workspace-relative wikilink 语义。
|
[Memory as File](./memory_as_file.md) 中的 workspace-relative wikilink 语义。
|
||||||
|
|
||||||
`auto_dream` 不凭空生成总览。只有 daily 输入中确实出现、并被抽取为 unit 或 topic 的内容,才会进入 digest 或
|
`auto_dream` 不凭空生成总览。只有 daily 输入中确实出现、并被抽取为 memory unit 的内容,才会进入 digest。
|
||||||
`interests.yaml`。
|
|
||||||
|
|
||||||
完整流程依赖 LLM 完成 Extract 和 Integrate。Topics 可以在没有 LLM 时做本地去重,但这不等于完整 dream 能离线运行。
|
完整流程依赖 LLM 完成 Extract、Integrate 和 Auto Tag。
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ auto_dream:
|
||||||
steps:
|
steps:
|
||||||
- dream_extract_step
|
- dream_extract_step
|
||||||
- dream_integrate_step # auto_link 的实际发生位置
|
- dream_integrate_step # auto_link 的实际发生位置
|
||||||
- dream_topics_step
|
|
||||||
- dream_finish_step
|
- dream_finish_step
|
||||||
|
- auto_tag_step
|
||||||
```
|
```
|
||||||
|
|
||||||
Integrate 阶段对每个 unit 独立运行。一个 unit 只落到一个目标 digest 节点,但这个目标节点可以链接多个来源和多个相关 digest
|
Integrate 阶段对每个 unit 独立运行。一个 unit 只落到一个目标 digest 节点,但这个目标节点可以链接多个来源和多个相关 digest
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,56 @@ session/
|
||||||
daily note 会指向对应的对话记录。持久化时会排除 tool-result block 和 base64 data block,避免召回记忆或二进制负载在后续流程中被误当成
|
daily note 会指向对应的对话记录。持久化时会排除 tool-result block 和 base64 data block,避免召回记忆或二进制负载在后续流程中被误当成
|
||||||
用户提供的证据。
|
用户提供的证据。
|
||||||
|
|
||||||
|
## 对话中的图像
|
||||||
|
|
||||||
|
Auto Memory 可以结合上下文理解对话中的图像。默认只处理文本,调用时加上 `include_images=true` 即可开启图像。
|
||||||
|
|
||||||
|
图像输入需要 `agentscope` wrapper,其 `as_llm` 应绑定支持视觉的模型,并使用兼容的 formatter。
|
||||||
|
Auto Memory 直接用这个模型理解图文,不先生成 caption。关闭图像或消息中没有图像块时,仍按原有方式处理文本,也不限制
|
||||||
|
wrapper 类型。
|
||||||
|
|
||||||
|
在 `messages` 中用 AgentScope 顶层 `DataBlock` 传入图像,媒体类型以 `image/` 开头。文本和图像按原顺序交错排列,
|
||||||
|
保留说话人和时间信息。Base64 source 与 HTTP(S) URL 原样交给 formatter,不缩放或转码。URL 不会被下载,需要能被模型
|
||||||
|
供应商访问;本地文件请先转为 Base64,不使用 `file://` URL,其他 URL scheme 也不支持。
|
||||||
|
|
||||||
|
开启图像后,Auto Memory 会把 Base64 图像的原始字节保存到配置的 `session_dir` 下:
|
||||||
|
|
||||||
|
```text
|
||||||
|
session/images/<session_id>/msg-<encoded-message-id>-image-<block-index>.<ext>
|
||||||
|
```
|
||||||
|
|
||||||
|
文件名使用消息的 `id`,以及图像在所有 content block 中的位置(从零开始),扩展名取自媒体类型。重复提交对话时,请保持
|
||||||
|
session ID、消息 ID 和 block 位置不变:同一路径已有文件会直接复用,不比较内容;更换图像时使用新的消息 ID。
|
||||||
|
关闭图像或消息中没有图像时,不保存附件。
|
||||||
|
|
||||||
|
模型输入中,每张图像旁边都会带上准确的来源链接。记忆 prompt 要求 Agent 在相应的视觉事实旁引用原图,例如:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
部署图中,Gateway 位于 Worker 和 PostgreSQL 之前,见 [[session/images/session-a/msg-6d6573736167652d61-image-1.png]]。
|
||||||
|
```
|
||||||
|
|
||||||
|
URL 图像使用原始 URL 作为引用。Auto Memory 还会把本次传入的图像来源补充到 daily note 的 `source_images` frontmatter 中,
|
||||||
|
保留已有条目。这个列表负责记录来源,正文链接则说明具体事实对应哪张图。会话附件不会被当作资源监听,也不会触发额外的 caption 调用。
|
||||||
|
|
||||||
|
每次调用的图像数量受 wrapper 的 `context_config.max_image_num` 限制,超限会报错,不会自动提高上限。
|
||||||
|
AgentScope 默认允许 5 张图像。需要更多时,在启动服务时设置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme start components.agent_wrapper.default.context_config.max_image_num=20
|
||||||
|
```
|
||||||
|
|
||||||
|
然后在另一个终端中,使用同一 workspace 调用已启动的服务:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
reme auto_memory session_id=session-a include_images=true messages='[...]'
|
||||||
|
```
|
||||||
|
|
||||||
|
模型与 formatter 自身的限制仍然适用。开启图像且消息中包含图像时,才会在保存对话前检查 wrapper backend、URL scheme 和图像数量。
|
||||||
|
之后的 formatter 或 provider 错误直接返回,不转为纯文本重试;与纯文本调用相同,已保存的对话不会因此回滚。
|
||||||
|
|
||||||
|
源 JSONL 仍按上文规则保存,包括过滤 Base64 block。读取 JSONL 时不会自动还原附件图像;再次处理图像仍需提交原始消息。
|
||||||
|
如果模型调用失败,或判断无需写入记忆卡片,已经保存的附件仍然保留,不会自动清理。本地来源路径也可以交给 `read_image` 读取。
|
||||||
|
|
||||||
## 消息时间
|
## 消息时间
|
||||||
|
|
||||||
Auto Memory 会在 prompt 和对话来源 JSONL 中保留每条已保留消息的 `created_at`。导入历史对话或 benchmark 数据时,建议为每条
|
Auto Memory 会在 prompt 和对话来源 JSONL 中保留每条已保留消息的 `created_at`。导入历史对话或 benchmark 数据时,建议为每条
|
||||||
|
|
@ -100,5 +150,11 @@ reme auto_memory \
|
||||||
|
|
||||||
## 后续流向
|
## 后续流向
|
||||||
|
|
||||||
|
默认的 `auto_memory` 和 `auto_memory_cc` Job 会在记录记忆后执行 `auto_tag_step`,只为实际新增或修改的 daily 笔记打标,
|
||||||
|
并使用重命名后的最终路径。Claude Code 调用方仍只需传入 `session_id`;重复 Stop 没有新增消息时,记忆生成和打标都会跳过。
|
||||||
|
|
||||||
|
标签描述文档的核心实体,写入配置的 frontmatter 字段,默认为 `memory_tags`。单文件打标失败记录在 `metadata.auto_tag`,
|
||||||
|
保留原有记忆响应;没有笔记变化的调用不会自动重试失败的打标。标签索引通过现有文件 watcher 异步更新。
|
||||||
|
|
||||||
Auto Memory 只生成 daily 层记忆。要把这些材料进一步沉淀为长期 `digest/` 节点,使用 [Auto Dream](./auto_dream.md);要搜索
|
Auto Memory 只生成 daily 层记忆。要把这些材料进一步沉淀为长期 `digest/` 节点,使用 [Auto Dream](./auto_dream.md);要搜索
|
||||||
daily 和 digest,使用 [Memory Search](./memory_search.md)。
|
daily 和 digest,使用 [Memory Search](./memory_search.md)。
|
||||||
|
|
|
||||||