everalgo-rank 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- everalgo_rank-0.1.0/.gitignore +58 -0
- everalgo_rank-0.1.0/CHANGELOG.md +30 -0
- everalgo_rank-0.1.0/LICENSE +21 -0
- everalgo_rank-0.1.0/PKG-INFO +114 -0
- everalgo_rank-0.1.0/README.md +94 -0
- everalgo_rank-0.1.0/pyproject.toml +31 -0
- everalgo_rank-0.1.0/src/everalgo/rank/__init__.py +76 -0
- everalgo_rank-0.1.0/src/everalgo/rank/case.py +87 -0
- everalgo_rank-0.1.0/src/everalgo/rank/episodic.py +87 -0
- everalgo_rank-0.1.0/src/everalgo/rank/fusion.py +595 -0
- everalgo_rank-0.1.0/src/everalgo/rank/profile.py +40 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/__init__.py +1 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/en/__init__.py +1 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/en/agentic.py +63 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/en/case.py +26 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/en/episodic.py +25 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/en/skill.py +25 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/zh/__init__.py +1 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/zh/agentic.py +59 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/zh/case.py +26 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/zh/episodic.py +25 -0
- everalgo_rank-0.1.0/src/everalgo/rank/prompts/zh/skill.py +25 -0
- everalgo_rank-0.1.0/src/everalgo/rank/py.typed +0 -0
- everalgo_rank-0.1.0/src/everalgo/rank/rerank.py +309 -0
- everalgo_rank-0.1.0/src/everalgo/rank/skill.py +87 -0
- everalgo_rank-0.1.0/src/everalgo/rank/weight.py +103 -0
- everalgo_rank-0.1.0/tests/rank/__init__.py +0 -0
- everalgo_rank-0.1.0/tests/rank/conftest.py +73 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_case.py +79 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_episodic.py +250 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_fusion.py +488 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_fusion_expand.py +212 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_profile.py +69 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_public_api.py +245 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_rerank.py +112 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_skill.py +53 -0
- everalgo_rank-0.1.0/tests/rank/test_rank_weight.py +159 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
wheels/
|
|
12
|
+
|
|
13
|
+
# Environments
|
|
14
|
+
.env
|
|
15
|
+
.env.local
|
|
16
|
+
.venv
|
|
17
|
+
env/
|
|
18
|
+
venv/
|
|
19
|
+
|
|
20
|
+
# uv
|
|
21
|
+
.uv/
|
|
22
|
+
|
|
23
|
+
# Test / coverage / cache
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.cache
|
|
26
|
+
.coverage
|
|
27
|
+
.coverage.*
|
|
28
|
+
coverage.xml
|
|
29
|
+
report.xml
|
|
30
|
+
htmlcov/
|
|
31
|
+
.tox/
|
|
32
|
+
.nox/
|
|
33
|
+
.hypothesis/
|
|
34
|
+
|
|
35
|
+
# Type checkers / linters
|
|
36
|
+
.mypy_cache/
|
|
37
|
+
.dmypy.json
|
|
38
|
+
.pyre/
|
|
39
|
+
.pytype/
|
|
40
|
+
.ruff_cache/
|
|
41
|
+
|
|
42
|
+
# Jupyter
|
|
43
|
+
.ipynb_checkpoints/
|
|
44
|
+
|
|
45
|
+
# IDE / OS
|
|
46
|
+
.vscode/
|
|
47
|
+
.idea/
|
|
48
|
+
*.swp
|
|
49
|
+
.DS_Store
|
|
50
|
+
|
|
51
|
+
# Logs
|
|
52
|
+
*.log
|
|
53
|
+
|
|
54
|
+
# Project local artifacts (placed under local/ per project convention)
|
|
55
|
+
local/
|
|
56
|
+
|
|
57
|
+
# Claude Code session-local state (scheduled tasks, agent caches, locks)
|
|
58
|
+
.claude/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this package are documented here. Format follows
|
|
4
|
+
[Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/). Versioning
|
|
5
|
+
follows [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `arank(rank_input, **kwargs) -> RankOutput`: async unified entry point; dispatches to the appropriate algorithm via an `_ALGO_REGISTRY` keyed by memory type.
|
|
12
|
+
- `rank(rank_input, **kwargs) -> RankOutput`: sync bridge via `asgiref.async_to_sync`.
|
|
13
|
+
- `EpisodicRanker`, `CaseRanker`, `SkillRanker`, `ProfileRanker`: facade classes that accept `llm=` at construction time and delegate to algorithm-layer functions.
|
|
14
|
+
- `aagentic_rank(query, candidates, *, llm, config) -> RankOutput`: multi-round agentic retrieval — LLM-guided sufficiency check followed by multi-query expansion, then RRF fusion over all rounds.
|
|
15
|
+
- `arerank(query, candidates, *, llm, prompt, config) -> list[Candidate]` and sync `rerank` bridge: LLM-scored pointwise reranker.
|
|
16
|
+
- `RankConfig` and `AgenticConfig` pydantic models for tuning rerank and agentic retrieval parameters.
|
|
17
|
+
- `FusionMode` enum: `rrf`, `lr`, `mrag`, `agentic`.
|
|
18
|
+
- `fusion.rrf(*sources, k) -> list[Candidate]`: Reciprocal Rank Fusion over multiple recall lists.
|
|
19
|
+
- `fusion.lr(candidates, *, weights) -> list[Candidate]`: linear-rank combination.
|
|
20
|
+
- `fusion.cosine_to_lr_score`, `fusion.score_propagation`, `fusion.expand`: score normalisation and episode-to-fact cross-memory expansion helpers.
|
|
21
|
+
- `weight` module: per-route weight configuration helpers.
|
|
22
|
+
- English rerank prompts for episodic, case, and skill memory types in `rank/prompts/en/`.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- Silent fallback returns in `_acheck_sufficiency` and `_agen_multi_queries`: both functions now raise on unexpected LLM response shapes instead of returning empty / default values.
|
|
27
|
+
- Silent fallback in `_apply_rerank_scores`: the function raises `ValueError` when the LLM response cannot be parsed into a valid score list.
|
|
28
|
+
- Outer `try/except` block in `aagentic_rank`: errors from sub-steps propagate to the caller unchanged.
|
|
29
|
+
|
|
30
|
+
[Unreleased]: https://github.com/EverMind-AI/EverAlgo/compare/main...HEAD
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EverMind
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: everalgo-rank
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: EverAlgo rank: 4 business-facing rankers (episodic / profile / case / skill) over a shared fusion / weight / rerank toolkit.
|
|
5
|
+
Project-URL: Homepage, https://github.com/EverMind-AI/EverAlgo
|
|
6
|
+
Project-URL: Repository, https://github.com/EverMind-AI/EverAlgo
|
|
7
|
+
Project-URL: Issues, https://github.com/EverMind-AI/EverAlgo/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/EverMind-AI/EverAlgo/tree/main/packages/everalgo-rank
|
|
9
|
+
Project-URL: Changelog, https://github.com/EverMind-AI/EverAlgo/blob/main/packages/everalgo-rank/CHANGELOG.md
|
|
10
|
+
Author: EverMind
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: asgiref>=3.0
|
|
18
|
+
Requires-Dist: everalgo-core<2.0.0,>=0.1.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# everalgo-rank
|
|
22
|
+
|
|
23
|
+
Memory ranking for EverAlgo — four business-facing ranker classes (`EpisodicRanker` / `CaseRanker` / `SkillRanker` / profile `rank`) composed over a shared toolkit (`fusion` / `weight` / `rerank`). Pure in-memory rank, no storage I/O.
|
|
24
|
+
|
|
25
|
+
See the umbrella project: [EverAlgo monorepo](../../README.md) and the architecture document at [`docs/concepts/architecture.md`](../../docs/concepts/architecture.md).
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install everalgo-rank
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## What this distribution provides
|
|
34
|
+
|
|
35
|
+
| Symbol | Role |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `EpisodicRanker` | Class facade — episodic memory ranking; LLM bound at construction |
|
|
38
|
+
| `CaseRanker` | Class facade — agent case ranking; LLM bound at construction |
|
|
39
|
+
| `SkillRanker` | Class facade — agent skill ranking; LLM bound at construction |
|
|
40
|
+
| `profile.rank` | Module-level sync function — profile ranking; cosine sort + dedup, no LLM |
|
|
41
|
+
| `RankConfig` | Frozen pydantic config: `fusion_mode`, `rrf_k`, `alpha`, etc. |
|
|
42
|
+
| `FusionMode` | Literal type: `"rrf"` / `"lr"` / `"mrag"` / `"agentic"` |
|
|
43
|
+
| `DEFAULT_RANK_CONFIG` | `RankConfig()` with defaults (`fusion_mode="rrf"`, `rrf_k=60`) |
|
|
44
|
+
| `arank` / `rank` | Top-level async / sync dispatch — routes to the registered ranker by `RankInput.memory_type` |
|
|
45
|
+
| `arerank` / `rerank` | Low-level LLM rerank step; used by the ranker facades internally |
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import asyncio
|
|
51
|
+
import json
|
|
52
|
+
|
|
53
|
+
from everalgo.llm.types import ChatResponse
|
|
54
|
+
from everalgo.rank import EpisodicRanker, DEFAULT_RANK_CONFIG
|
|
55
|
+
from everalgo.testing.fake_llm import FakeLLMClient
|
|
56
|
+
from everalgo.types import Candidate, RankInput
|
|
57
|
+
|
|
58
|
+
_RERANK_JSON = json.dumps({"ranked": [{"id": "ep_a", "score": 0.95}]})
|
|
59
|
+
|
|
60
|
+
async def main() -> None:
|
|
61
|
+
fake = FakeLLMClient(responses=[ChatResponse(content=_RERANK_JSON, model="fake")])
|
|
62
|
+
ranker = EpisodicRanker(llm=fake)
|
|
63
|
+
|
|
64
|
+
rank_input = RankInput(
|
|
65
|
+
query="Python async retry patterns",
|
|
66
|
+
memory_type="episodic",
|
|
67
|
+
dense_candidates=[
|
|
68
|
+
Candidate(id="ep_a", score=0.9, metadata={"episode": "Alice asked about async retries."}),
|
|
69
|
+
Candidate(id="ep_b", score=0.5, metadata={"episode": "Team discussed lunch."}),
|
|
70
|
+
],
|
|
71
|
+
top_k=1,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
output = await ranker.arank(rank_input, config=DEFAULT_RANK_CONFIG, enable_rerank=True)
|
|
75
|
+
for item in output.items:
|
|
76
|
+
print(f"{item.id} score={item.score:.2f}")
|
|
77
|
+
|
|
78
|
+
asyncio.run(main())
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## LLM rerank step
|
|
82
|
+
|
|
83
|
+
`arerank` is the standalone LLM reranking primitive used internally by the ranker facades and exposed for direct use:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from everalgo.rank.rerank import arerank
|
|
87
|
+
from everalgo.rank.prompts.en.episodic import EPISODIC_RERANK_PROMPT_EN
|
|
88
|
+
from everalgo.types import Candidate
|
|
89
|
+
|
|
90
|
+
reranked = await arerank(
|
|
91
|
+
candidates,
|
|
92
|
+
prompt=EPISODIC_RERANK_PROMPT_EN,
|
|
93
|
+
top_k=5,
|
|
94
|
+
llm=client,
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## API surface
|
|
99
|
+
|
|
100
|
+
| Symbol | Module | Signature summary |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `EpisodicRanker` | `everalgo.rank` | `__init__(*, llm)` → `arank(rank_input, *, config, prompt, enable_rerank, ...) → RankOutput` |
|
|
103
|
+
| `CaseRanker` | `everalgo.rank` | Same shape as `EpisodicRanker` |
|
|
104
|
+
| `SkillRanker` | `everalgo.rank` | Same shape as `EpisodicRanker` |
|
|
105
|
+
| `profile.rank` | `everalgo.rank.profile` | `(rank_input, *, threshold=0.0) → RankOutput` — sync, no LLM |
|
|
106
|
+
| `RankConfig` | `everalgo.rank` | `fusion_mode: FusionMode = "rrf"`, `rrf_k: int = 60`, `alpha: float = 1.0`, `expand_limit: int = 3` |
|
|
107
|
+
| `arank` | `everalgo.rank` | Top-level async dispatch by `rank_input.memory_type` |
|
|
108
|
+
| `arerank` | `everalgo.rank.rerank` | `(items, *, prompt, top_k, llm) → list[Candidate]` |
|
|
109
|
+
|
|
110
|
+
## Cross-links
|
|
111
|
+
|
|
112
|
+
- [`everalgo-core`](../everalgo-core/) — `RankInput`, `RankOutput`, `Candidate`, `ScoredItem`, `LLMClient`
|
|
113
|
+
- [`everalgo-user-memory`](../everalgo-user-memory/) — produces `Episode` / `AtomicFact` / `Profile` that EverOS Recall packages into `Candidate` for ranking
|
|
114
|
+
- [`everalgo-agent-memory`](../everalgo-agent-memory/) — produces `AgentCase` / `AgentSkill` candidates
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# everalgo-rank
|
|
2
|
+
|
|
3
|
+
Memory ranking for EverAlgo — four business-facing ranker classes (`EpisodicRanker` / `CaseRanker` / `SkillRanker` / profile `rank`) composed over a shared toolkit (`fusion` / `weight` / `rerank`). Pure in-memory rank, no storage I/O.
|
|
4
|
+
|
|
5
|
+
See the umbrella project: [EverAlgo monorepo](../../README.md) and the architecture document at [`docs/concepts/architecture.md`](../../docs/concepts/architecture.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install everalgo-rank
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What this distribution provides
|
|
14
|
+
|
|
15
|
+
| Symbol | Role |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `EpisodicRanker` | Class facade — episodic memory ranking; LLM bound at construction |
|
|
18
|
+
| `CaseRanker` | Class facade — agent case ranking; LLM bound at construction |
|
|
19
|
+
| `SkillRanker` | Class facade — agent skill ranking; LLM bound at construction |
|
|
20
|
+
| `profile.rank` | Module-level sync function — profile ranking; cosine sort + dedup, no LLM |
|
|
21
|
+
| `RankConfig` | Frozen pydantic config: `fusion_mode`, `rrf_k`, `alpha`, etc. |
|
|
22
|
+
| `FusionMode` | Literal type: `"rrf"` / `"lr"` / `"mrag"` / `"agentic"` |
|
|
23
|
+
| `DEFAULT_RANK_CONFIG` | `RankConfig()` with defaults (`fusion_mode="rrf"`, `rrf_k=60`) |
|
|
24
|
+
| `arank` / `rank` | Top-level async / sync dispatch — routes to the registered ranker by `RankInput.memory_type` |
|
|
25
|
+
| `arerank` / `rerank` | Low-level LLM rerank step; used by the ranker facades internally |
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import asyncio
|
|
31
|
+
import json
|
|
32
|
+
|
|
33
|
+
from everalgo.llm.types import ChatResponse
|
|
34
|
+
from everalgo.rank import EpisodicRanker, DEFAULT_RANK_CONFIG
|
|
35
|
+
from everalgo.testing.fake_llm import FakeLLMClient
|
|
36
|
+
from everalgo.types import Candidate, RankInput
|
|
37
|
+
|
|
38
|
+
_RERANK_JSON = json.dumps({"ranked": [{"id": "ep_a", "score": 0.95}]})
|
|
39
|
+
|
|
40
|
+
async def main() -> None:
|
|
41
|
+
fake = FakeLLMClient(responses=[ChatResponse(content=_RERANK_JSON, model="fake")])
|
|
42
|
+
ranker = EpisodicRanker(llm=fake)
|
|
43
|
+
|
|
44
|
+
rank_input = RankInput(
|
|
45
|
+
query="Python async retry patterns",
|
|
46
|
+
memory_type="episodic",
|
|
47
|
+
dense_candidates=[
|
|
48
|
+
Candidate(id="ep_a", score=0.9, metadata={"episode": "Alice asked about async retries."}),
|
|
49
|
+
Candidate(id="ep_b", score=0.5, metadata={"episode": "Team discussed lunch."}),
|
|
50
|
+
],
|
|
51
|
+
top_k=1,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
output = await ranker.arank(rank_input, config=DEFAULT_RANK_CONFIG, enable_rerank=True)
|
|
55
|
+
for item in output.items:
|
|
56
|
+
print(f"{item.id} score={item.score:.2f}")
|
|
57
|
+
|
|
58
|
+
asyncio.run(main())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## LLM rerank step
|
|
62
|
+
|
|
63
|
+
`arerank` is the standalone LLM reranking primitive used internally by the ranker facades and exposed for direct use:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from everalgo.rank.rerank import arerank
|
|
67
|
+
from everalgo.rank.prompts.en.episodic import EPISODIC_RERANK_PROMPT_EN
|
|
68
|
+
from everalgo.types import Candidate
|
|
69
|
+
|
|
70
|
+
reranked = await arerank(
|
|
71
|
+
candidates,
|
|
72
|
+
prompt=EPISODIC_RERANK_PROMPT_EN,
|
|
73
|
+
top_k=5,
|
|
74
|
+
llm=client,
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API surface
|
|
79
|
+
|
|
80
|
+
| Symbol | Module | Signature summary |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `EpisodicRanker` | `everalgo.rank` | `__init__(*, llm)` → `arank(rank_input, *, config, prompt, enable_rerank, ...) → RankOutput` |
|
|
83
|
+
| `CaseRanker` | `everalgo.rank` | Same shape as `EpisodicRanker` |
|
|
84
|
+
| `SkillRanker` | `everalgo.rank` | Same shape as `EpisodicRanker` |
|
|
85
|
+
| `profile.rank` | `everalgo.rank.profile` | `(rank_input, *, threshold=0.0) → RankOutput` — sync, no LLM |
|
|
86
|
+
| `RankConfig` | `everalgo.rank` | `fusion_mode: FusionMode = "rrf"`, `rrf_k: int = 60`, `alpha: float = 1.0`, `expand_limit: int = 3` |
|
|
87
|
+
| `arank` | `everalgo.rank` | Top-level async dispatch by `rank_input.memory_type` |
|
|
88
|
+
| `arerank` | `everalgo.rank.rerank` | `(items, *, prompt, top_k, llm) → list[Candidate]` |
|
|
89
|
+
|
|
90
|
+
## Cross-links
|
|
91
|
+
|
|
92
|
+
- [`everalgo-core`](../everalgo-core/) — `RankInput`, `RankOutput`, `Candidate`, `ScoredItem`, `LLMClient`
|
|
93
|
+
- [`everalgo-user-memory`](../everalgo-user-memory/) — produces `Episode` / `AtomicFact` / `Profile` that EverOS Recall packages into `Candidate` for ranking
|
|
94
|
+
- [`everalgo-agent-memory`](../everalgo-agent-memory/) — produces `AgentCase` / `AgentSkill` candidates
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "everalgo-rank"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "EverAlgo rank: 4 business-facing rankers (episodic / profile / case / skill) over a shared fusion / weight / rerank toolkit."
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
authors = [{ name = "EverMind" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"asgiref>=3.0",
|
|
20
|
+
"everalgo-core>=0.1.0,<2.0.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/EverMind-AI/EverAlgo"
|
|
25
|
+
Repository = "https://github.com/EverMind-AI/EverAlgo"
|
|
26
|
+
Issues = "https://github.com/EverMind-AI/EverAlgo/issues"
|
|
27
|
+
Documentation = "https://github.com/EverMind-AI/EverAlgo/tree/main/packages/everalgo-rank"
|
|
28
|
+
Changelog = "https://github.com/EverMind-AI/EverAlgo/blob/main/packages/everalgo-rank/CHANGELOG.md"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
packages = ["src/everalgo"]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Memory ranking — public surface of the rank package."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from asgiref.sync import sync_to_async
|
|
6
|
+
|
|
7
|
+
from everalgo.rank import case, episodic, fusion, profile, rerank, skill, weight
|
|
8
|
+
from everalgo.rank.case import CaseRanker
|
|
9
|
+
from everalgo.rank.episodic import EpisodicRanker
|
|
10
|
+
from everalgo.rank.prompts.en.case import CASE_RERANK_PROMPT_EN
|
|
11
|
+
from everalgo.rank.prompts.en.episodic import EPISODIC_RERANK_PROMPT_EN
|
|
12
|
+
from everalgo.rank.prompts.en.skill import SKILL_RERANK_PROMPT_EN
|
|
13
|
+
from everalgo.rank.rerank import (
|
|
14
|
+
_ALGO_REGISTRY,
|
|
15
|
+
DEFAULT_RANK_CONFIG,
|
|
16
|
+
FusionMode,
|
|
17
|
+
RankConfig,
|
|
18
|
+
_RankerSpec,
|
|
19
|
+
)
|
|
20
|
+
from everalgo.rank.rerank import (
|
|
21
|
+
_arank as arank,
|
|
22
|
+
)
|
|
23
|
+
from everalgo.rank.rerank import (
|
|
24
|
+
_rank as rank,
|
|
25
|
+
)
|
|
26
|
+
from everalgo.rank.skill import SkillRanker
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"DEFAULT_RANK_CONFIG",
|
|
30
|
+
"CaseRanker",
|
|
31
|
+
"EpisodicRanker",
|
|
32
|
+
"FusionMode",
|
|
33
|
+
"RankConfig",
|
|
34
|
+
"SkillRanker",
|
|
35
|
+
"arank",
|
|
36
|
+
"case",
|
|
37
|
+
"episodic",
|
|
38
|
+
"fusion",
|
|
39
|
+
"profile",
|
|
40
|
+
"rank",
|
|
41
|
+
"rerank",
|
|
42
|
+
"skill",
|
|
43
|
+
"weight",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# Library logging setup (ADR-013): NullHandler on each subpackage logger.
|
|
47
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
48
|
+
|
|
49
|
+
_ALGO_REGISTRY.update(
|
|
50
|
+
{
|
|
51
|
+
"case": _RankerSpec(
|
|
52
|
+
arank=case.arank,
|
|
53
|
+
modes=("rrf", "lr", "agentic"),
|
|
54
|
+
rerank_prompt=CASE_RERANK_PROMPT_EN,
|
|
55
|
+
item_type="case",
|
|
56
|
+
),
|
|
57
|
+
"skill": _RankerSpec(
|
|
58
|
+
arank=skill.arank,
|
|
59
|
+
modes=("rrf", "lr", "agentic"),
|
|
60
|
+
rerank_prompt=SKILL_RERANK_PROMPT_EN,
|
|
61
|
+
item_type="skill",
|
|
62
|
+
),
|
|
63
|
+
"episodic": _RankerSpec(
|
|
64
|
+
arank=episodic.arank,
|
|
65
|
+
modes=("rrf", "lr", "mrag", "agentic"),
|
|
66
|
+
rerank_prompt=EPISODIC_RERANK_PROMPT_EN,
|
|
67
|
+
item_type="episode",
|
|
68
|
+
),
|
|
69
|
+
"profile": _RankerSpec(
|
|
70
|
+
arank=sync_to_async(profile.rank),
|
|
71
|
+
modes=(),
|
|
72
|
+
rerank_prompt="",
|
|
73
|
+
item_type="profile",
|
|
74
|
+
),
|
|
75
|
+
}
|
|
76
|
+
)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Case ranker facade — thin wrapper over ``rerank._basic_arank``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from asgiref.sync import async_to_sync
|
|
8
|
+
|
|
9
|
+
from everalgo.rank.rerank import DEFAULT_RANK_CONFIG, RankConfig, _basic_arank
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from everalgo.llm.protocols import LLMClient
|
|
13
|
+
from everalgo.rank.fusion import RerankFn, RetrieveFn
|
|
14
|
+
from everalgo.types import RankInput, RankOutput
|
|
15
|
+
|
|
16
|
+
__all__ = ["CaseRanker"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CaseRanker:
|
|
20
|
+
"""Class-style facade for the case ranker.
|
|
21
|
+
|
|
22
|
+
The LLM client is bound to the instance at construction time.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, *, llm: LLMClient) -> None:
|
|
26
|
+
self._llm = llm
|
|
27
|
+
|
|
28
|
+
async def arank(
|
|
29
|
+
self,
|
|
30
|
+
rank_input: RankInput,
|
|
31
|
+
*,
|
|
32
|
+
config: RankConfig = DEFAULT_RANK_CONFIG,
|
|
33
|
+
prompt: str | None = None,
|
|
34
|
+
enable_rerank: bool = False,
|
|
35
|
+
retrieve_fn: RetrieveFn | None = None,
|
|
36
|
+
rerank_fn: RerankFn | None = None,
|
|
37
|
+
) -> RankOutput:
|
|
38
|
+
"""Case ranker — see ``rerank._basic_arank`` for the pipeline body.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
rank_input: Query + candidate sets for agent-case memory ranking.
|
|
42
|
+
config: Fusion mode and hyperparameters.
|
|
43
|
+
prompt: Per-call rerank prompt override; ``None`` uses the built-in default.
|
|
44
|
+
enable_rerank: When ``True``, run the LLM rerank stage after fusion.
|
|
45
|
+
retrieve_fn: Optional retrieval callback for the ``'agentic'`` fusion mode Round 2.
|
|
46
|
+
rerank_fn: Required for ``fusion_mode='agentic'``; cross-encoder callback for Round 1 rerank.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Ranked and optionally LLM-reranked case items.
|
|
50
|
+
"""
|
|
51
|
+
return await _basic_arank(
|
|
52
|
+
rank_input,
|
|
53
|
+
config=config,
|
|
54
|
+
llm=self._llm,
|
|
55
|
+
prompt=prompt,
|
|
56
|
+
enable_rerank=enable_rerank,
|
|
57
|
+
retrieve_fn=retrieve_fn,
|
|
58
|
+
rerank_fn=rerank_fn,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
rank = async_to_sync(arank)
|
|
62
|
+
"""Sync bridge — only callable from non-event-loop contexts."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def arank(
|
|
66
|
+
rank_input: RankInput,
|
|
67
|
+
*,
|
|
68
|
+
config: RankConfig = DEFAULT_RANK_CONFIG,
|
|
69
|
+
llm: LLMClient | None = None,
|
|
70
|
+
prompt: str | None = None,
|
|
71
|
+
enable_rerank: bool = False,
|
|
72
|
+
retrieve_fn: RetrieveFn | None = None,
|
|
73
|
+
rerank_fn: RerankFn | None = None,
|
|
74
|
+
) -> RankOutput:
|
|
75
|
+
"""Case module-level ranker — delegates to ``rerank._basic_arank``."""
|
|
76
|
+
return await _basic_arank(
|
|
77
|
+
rank_input,
|
|
78
|
+
config=config,
|
|
79
|
+
llm=llm,
|
|
80
|
+
prompt=prompt,
|
|
81
|
+
enable_rerank=enable_rerank,
|
|
82
|
+
retrieve_fn=retrieve_fn,
|
|
83
|
+
rerank_fn=rerank_fn,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
rank = async_to_sync(arank)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Episodic ranker facade — thin wrapper over ``rerank._basic_arank``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from asgiref.sync import async_to_sync
|
|
8
|
+
|
|
9
|
+
from everalgo.rank.rerank import DEFAULT_RANK_CONFIG, RankConfig, _basic_arank
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from everalgo.llm.protocols import LLMClient
|
|
13
|
+
from everalgo.rank.fusion import RerankFn, RetrieveFn
|
|
14
|
+
from everalgo.types import RankInput, RankOutput
|
|
15
|
+
|
|
16
|
+
__all__ = ["EpisodicRanker"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EpisodicRanker:
|
|
20
|
+
"""Class-style facade for the episodic ranker.
|
|
21
|
+
|
|
22
|
+
The LLM client is bound to the instance at construction time.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, *, llm: LLMClient) -> None:
|
|
26
|
+
self._llm = llm
|
|
27
|
+
|
|
28
|
+
async def arank(
|
|
29
|
+
self,
|
|
30
|
+
rank_input: RankInput,
|
|
31
|
+
*,
|
|
32
|
+
config: RankConfig = DEFAULT_RANK_CONFIG,
|
|
33
|
+
prompt: str | None = None,
|
|
34
|
+
enable_rerank: bool = False,
|
|
35
|
+
retrieve_fn: RetrieveFn | None = None,
|
|
36
|
+
rerank_fn: RerankFn | None = None,
|
|
37
|
+
) -> RankOutput:
|
|
38
|
+
"""Episodic ranker — see ``rerank._basic_arank`` for the pipeline body.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
rank_input: Query + candidate sets for episodic memory ranking.
|
|
42
|
+
config: Fusion mode and hyperparameters.
|
|
43
|
+
prompt: Per-call rerank prompt override; ``None`` uses the built-in default.
|
|
44
|
+
enable_rerank: When ``True``, run the LLM rerank stage after fusion.
|
|
45
|
+
retrieve_fn: Optional retrieval callback for the ``'agentic'`` fusion mode Round 2.
|
|
46
|
+
rerank_fn: Required for ``fusion_mode='agentic'``; cross-encoder callback for Round 1 rerank.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Ranked and optionally LLM-reranked episodic items.
|
|
50
|
+
"""
|
|
51
|
+
return await _basic_arank(
|
|
52
|
+
rank_input,
|
|
53
|
+
config=config,
|
|
54
|
+
llm=self._llm,
|
|
55
|
+
prompt=prompt,
|
|
56
|
+
enable_rerank=enable_rerank,
|
|
57
|
+
retrieve_fn=retrieve_fn,
|
|
58
|
+
rerank_fn=rerank_fn,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
rank = async_to_sync(arank)
|
|
62
|
+
"""Sync bridge — only callable from non-event-loop contexts."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def arank(
|
|
66
|
+
rank_input: RankInput,
|
|
67
|
+
*,
|
|
68
|
+
config: RankConfig = DEFAULT_RANK_CONFIG,
|
|
69
|
+
llm: LLMClient | None = None,
|
|
70
|
+
prompt: str | None = None,
|
|
71
|
+
enable_rerank: bool = False,
|
|
72
|
+
retrieve_fn: RetrieveFn | None = None,
|
|
73
|
+
rerank_fn: RerankFn | None = None,
|
|
74
|
+
) -> RankOutput:
|
|
75
|
+
"""Episodic module-level ranker — delegates to ``rerank._basic_arank``."""
|
|
76
|
+
return await _basic_arank(
|
|
77
|
+
rank_input,
|
|
78
|
+
config=config,
|
|
79
|
+
llm=llm,
|
|
80
|
+
prompt=prompt,
|
|
81
|
+
enable_rerank=enable_rerank,
|
|
82
|
+
retrieve_fn=retrieve_fn,
|
|
83
|
+
rerank_fn=rerank_fn,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
rank = async_to_sync(arank)
|