icelake 0.5.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.
- icelake-0.5.0/.github/workflows/ci.yml +80 -0
- icelake-0.5.0/.github/workflows/publish.yml +73 -0
- icelake-0.5.0/.github/workflows/release.yml +100 -0
- icelake-0.5.0/.gitignore +24 -0
- icelake-0.5.0/.python-version +1 -0
- icelake-0.5.0/AGENTS.md +76 -0
- icelake-0.5.0/CHANGELOG.md +129 -0
- icelake-0.5.0/LICENSE +21 -0
- icelake-0.5.0/PKG-INFO +698 -0
- icelake-0.5.0/README.md +665 -0
- icelake-0.5.0/changelog.d/.gitignore +1 -0
- icelake-0.5.0/evals/golden/adversarial_injection.yaml +8 -0
- icelake-0.5.0/evals/golden/ambiguous_identity.yaml +14 -0
- icelake-0.5.0/evals/golden/bare_link_skip.yaml +7 -0
- icelake-0.5.0/evals/golden/contradiction_supersede.yaml +14 -0
- icelake-0.5.0/evals/golden/cross_user_isolation.yaml +21 -0
- icelake-0.5.0/evals/golden/durable_preference.yaml +14 -0
- icelake-0.5.0/evals/golden/mention_extraction.yaml +18 -0
- icelake-0.5.0/evals/golden/reinforce_not_duplicate.yaml +15 -0
- icelake-0.5.0/evals/golden/server_culture.yaml +14 -0
- icelake-0.5.0/evals/golden/snowflake_poisoning.yaml +7 -0
- icelake-0.5.0/evals/golden/third_party_attribution.yaml +18 -0
- icelake-0.5.0/evals/golden/transient_state_skip.yaml +9 -0
- icelake-0.5.0/evals/golden/write_create_biographical.yaml +14 -0
- icelake-0.5.0/evals/golden/write_skip_noise.yaml +9 -0
- icelake-0.5.0/evals/golden/write_skip_questions.yaml +9 -0
- icelake-0.5.0/evals/golden_runner.py +176 -0
- icelake-0.5.0/examples/bench_models.py +264 -0
- icelake-0.5.0/examples/e2e_simulation.py +1444 -0
- icelake-0.5.0/examples/name_lookup_tool.py +226 -0
- icelake-0.5.0/examples/omni_style_bot.py +543 -0
- icelake-0.5.0/examples/ping_reply_bot.py +291 -0
- icelake-0.5.0/examples/relationship_queries.py +290 -0
- icelake-0.5.0/pyproject.toml +118 -0
- icelake-0.5.0/src/icelake/__init__.py +249 -0
- icelake-0.5.0/src/icelake/_json.py +125 -0
- icelake-0.5.0/src/icelake/adapters/__init__.py +1 -0
- icelake-0.5.0/src/icelake/adapters/embedders/__init__.py +126 -0
- icelake-0.5.0/src/icelake/adapters/embedders/cached.py +74 -0
- icelake-0.5.0/src/icelake/adapters/embedders/local.py +51 -0
- icelake-0.5.0/src/icelake/adapters/in_memory/__init__.py +10 -0
- icelake-0.5.0/src/icelake/adapters/in_memory/queue.py +245 -0
- icelake-0.5.0/src/icelake/adapters/in_memory/store.py +930 -0
- icelake-0.5.0/src/icelake/adapters/in_memory/vectors.py +58 -0
- icelake-0.5.0/src/icelake/adapters/llm_cache.py +51 -0
- icelake-0.5.0/src/icelake/adapters/llm_openai_compat.py +235 -0
- icelake-0.5.0/src/icelake/adapters/llm_openrouter.py +41 -0
- icelake-0.5.0/src/icelake/adapters/meter.py +167 -0
- icelake-0.5.0/src/icelake/adapters/mongo/__init__.py +1272 -0
- icelake-0.5.0/src/icelake/adapters/mongo/mapping.py +268 -0
- icelake-0.5.0/src/icelake/adapters/mongo/queue.py +300 -0
- icelake-0.5.0/src/icelake/adapters/mongo/vectors.py +109 -0
- icelake-0.5.0/src/icelake/adapters/rerankers.py +121 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/connection.py +385 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/llm_cache.py +40 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/queue.py +315 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/store.py +68 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/store_facts.py +857 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/store_graph.py +590 -0
- icelake-0.5.0/src/icelake/adapters/sqlite/vectors.py +120 -0
- icelake-0.5.0/src/icelake/api/classify.py +88 -0
- icelake-0.5.0/src/icelake/api/client.py +879 -0
- icelake-0.5.0/src/icelake/api/events.py +73 -0
- icelake-0.5.0/src/icelake/api/facts_api.py +352 -0
- icelake-0.5.0/src/icelake/api/groups.py +382 -0
- icelake-0.5.0/src/icelake/attribution.py +155 -0
- icelake-0.5.0/src/icelake/citations.py +250 -0
- icelake-0.5.0/src/icelake/config.py +395 -0
- icelake-0.5.0/src/icelake/consolidation/service.py +158 -0
- icelake-0.5.0/src/icelake/errors.py +60 -0
- icelake-0.5.0/src/icelake/graph/__init__.py +5 -0
- icelake-0.5.0/src/icelake/graph/collapse.py +54 -0
- icelake-0.5.0/src/icelake/graph/relations.py +87 -0
- icelake-0.5.0/src/icelake/graph/traversal.py +102 -0
- icelake-0.5.0/src/icelake/graph/writes.py +234 -0
- icelake-0.5.0/src/icelake/identity/__init__.py +6 -0
- icelake-0.5.0/src/icelake/identity/aliases.py +169 -0
- icelake-0.5.0/src/icelake/identity/guards.py +63 -0
- icelake-0.5.0/src/icelake/identity/resolver.py +109 -0
- icelake-0.5.0/src/icelake/ids.py +20 -0
- icelake-0.5.0/src/icelake/ingest/__init__.py +5 -0
- icelake-0.5.0/src/icelake/ingest/context_builder.py +63 -0
- icelake-0.5.0/src/icelake/ingest/executor.py +314 -0
- icelake-0.5.0/src/icelake/ingest/extraction.py +142 -0
- icelake-0.5.0/src/icelake/ingest/gates.py +186 -0
- icelake-0.5.0/src/icelake/ingest/pipeline.py +791 -0
- icelake-0.5.0/src/icelake/ingest/reconcile.py +271 -0
- icelake-0.5.0/src/icelake/ingest/roster.py +98 -0
- icelake-0.5.0/src/icelake/integrations/__init__.py +5 -0
- icelake-0.5.0/src/icelake/integrations/discord_py.py +233 -0
- icelake-0.5.0/src/icelake/lifecycle/__init__.py +15 -0
- icelake-0.5.0/src/icelake/lifecycle/forget.py +48 -0
- icelake-0.5.0/src/icelake/lifecycle/maintenance.py +92 -0
- icelake-0.5.0/src/icelake/lifecycle/prune.py +50 -0
- icelake-0.5.0/src/icelake/lifecycle/strength.py +62 -0
- icelake-0.5.0/src/icelake/lifecycle/tiers.py +86 -0
- icelake-0.5.0/src/icelake/models/__init__.py +129 -0
- icelake-0.5.0/src/icelake/models/admin.py +116 -0
- icelake-0.5.0/src/icelake/models/common.py +46 -0
- icelake-0.5.0/src/icelake/models/events.py +129 -0
- icelake-0.5.0/src/icelake/models/facts.py +187 -0
- icelake-0.5.0/src/icelake/models/graph.py +158 -0
- icelake-0.5.0/src/icelake/models/identity.py +75 -0
- icelake-0.5.0/src/icelake/models/operations.py +112 -0
- icelake-0.5.0/src/icelake/models/retrieval.py +190 -0
- icelake-0.5.0/src/icelake/ports/__init__.py +48 -0
- icelake-0.5.0/src/icelake/ports/clock.py +51 -0
- icelake-0.5.0/src/icelake/ports/llm.py +124 -0
- icelake-0.5.0/src/icelake/ports/queue.py +133 -0
- icelake-0.5.0/src/icelake/ports/store.py +425 -0
- icelake-0.5.0/src/icelake/ports/vectors.py +64 -0
- icelake-0.5.0/src/icelake/prompts/__init__.py +1 -0
- icelake-0.5.0/src/icelake/prompts/extraction.py +106 -0
- icelake-0.5.0/src/icelake/py.typed +0 -0
- icelake-0.5.0/src/icelake/retrieval/__init__.py +5 -0
- icelake-0.5.0/src/icelake/retrieval/channels.py +223 -0
- icelake-0.5.0/src/icelake/retrieval/injection.py +196 -0
- icelake-0.5.0/src/icelake/retrieval/service.py +432 -0
- icelake-0.5.0/src/icelake/scoring/__init__.py +5 -0
- icelake-0.5.0/src/icelake/scoring/fusion.py +103 -0
- icelake-0.5.0/src/icelake/structured.py +76 -0
- icelake-0.5.0/src/icelake/visualizer/__init__.py +18 -0
- icelake-0.5.0/src/icelake/visualizer/__main__.py +124 -0
- icelake-0.5.0/src/icelake/visualizer/html.py +693 -0
- icelake-0.5.0/src/icelake/visualizer/models.py +98 -0
- icelake-0.5.0/src/icelake/visualizer/snapshot.py +331 -0
- icelake-0.5.0/tests/__init__.py +0 -0
- icelake-0.5.0/tests/conftest.py +160 -0
- icelake-0.5.0/tests/integration/__init__.py +0 -0
- icelake-0.5.0/tests/integration/test_adversarial.py +350 -0
- icelake-0.5.0/tests/integration/test_agentic_e2e.py +464 -0
- icelake-0.5.0/tests/integration/test_api_groups.py +139 -0
- icelake-0.5.0/tests/integration/test_coverage_backends.py +309 -0
- icelake-0.5.0/tests/integration/test_decay_loop.py +109 -0
- icelake-0.5.0/tests/integration/test_discord_integration.py +225 -0
- icelake-0.5.0/tests/integration/test_examples.py +214 -0
- icelake-0.5.0/tests/integration/test_golden_evals.py +18 -0
- icelake-0.5.0/tests/integration/test_graph_recall.py +122 -0
- icelake-0.5.0/tests/integration/test_hardening_round.py +614 -0
- icelake-0.5.0/tests/integration/test_import_export.py +58 -0
- icelake-0.5.0/tests/integration/test_mongo_live.py +208 -0
- icelake-0.5.0/tests/integration/test_mongo_vectors.py +74 -0
- icelake-0.5.0/tests/integration/test_phase0.py +293 -0
- icelake-0.5.0/tests/integration/test_pipeline_e2e.py +802 -0
- icelake-0.5.0/tests/integration/test_queue_conformance.py +126 -0
- icelake-0.5.0/tests/integration/test_recall_prompt.py +316 -0
- icelake-0.5.0/tests/integration/test_review_round.py +533 -0
- icelake-0.5.0/tests/integration/test_store_conformance.py +537 -0
- icelake-0.5.0/tests/unit/__init__.py +0 -0
- icelake-0.5.0/tests/unit/test_adapters.py +477 -0
- icelake-0.5.0/tests/unit/test_adapters_edge.py +224 -0
- icelake-0.5.0/tests/unit/test_citations.py +254 -0
- icelake-0.5.0/tests/unit/test_config.py +197 -0
- icelake-0.5.0/tests/unit/test_context_builder.py +81 -0
- icelake-0.5.0/tests/unit/test_coverage_completion.py +525 -0
- icelake-0.5.0/tests/unit/test_coverage_final.py +465 -0
- icelake-0.5.0/tests/unit/test_coverage_gaps.py +181 -0
- icelake-0.5.0/tests/unit/test_executor.py +313 -0
- icelake-0.5.0/tests/unit/test_extraction.py +296 -0
- icelake-0.5.0/tests/unit/test_gates.py +111 -0
- icelake-0.5.0/tests/unit/test_identity.py +167 -0
- icelake-0.5.0/tests/unit/test_identity_collapse.py +324 -0
- icelake-0.5.0/tests/unit/test_ingest_debug.py +92 -0
- icelake-0.5.0/tests/unit/test_injection_roster.py +248 -0
- icelake-0.5.0/tests/unit/test_lifecycle.py +303 -0
- icelake-0.5.0/tests/unit/test_models.py +123 -0
- icelake-0.5.0/tests/unit/test_mongo_adapter.py +176 -0
- icelake-0.5.0/tests/unit/test_reconcile.py +253 -0
- icelake-0.5.0/tests/unit/test_reranker_citations.py +324 -0
- icelake-0.5.0/tests/unit/test_scoring_graph.py +187 -0
- icelake-0.5.0/tests/unit/test_structured.py +56 -0
- icelake-0.5.0/tests/unit/test_structured_outputs.py +182 -0
- icelake-0.5.0/tests/unit/test_transactions.py +122 -0
- icelake-0.5.0/tests/unit/test_vectors.py +62 -0
- icelake-0.5.0/tests/unit/test_visualizer.py +366 -0
- icelake-0.5.0/uv.lock +2322 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v6
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: uv sync --group dev
|
|
17
|
+
- run: uv run ruff check src tests
|
|
18
|
+
- run: uv run ruff format --check src tests
|
|
19
|
+
|
|
20
|
+
typecheck:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
- run: uv sync --group dev
|
|
28
|
+
- run: uv run mypy
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
strategy:
|
|
33
|
+
matrix:
|
|
34
|
+
python-version: ["3.12", "3.13"]
|
|
35
|
+
services:
|
|
36
|
+
mongodb:
|
|
37
|
+
image: mongo:7
|
|
38
|
+
ports:
|
|
39
|
+
- 27017:27017
|
|
40
|
+
options: >-
|
|
41
|
+
--health-cmd "mongosh --eval 'db.adminCommand({ ping: 1 })'"
|
|
42
|
+
--health-interval 10s
|
|
43
|
+
--health-timeout 5s
|
|
44
|
+
--health-retries 10
|
|
45
|
+
--health-start-period 20s
|
|
46
|
+
env:
|
|
47
|
+
MONGODB_URI: mongodb://127.0.0.1:27017/icelake_test
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
- uses: astral-sh/setup-uv@v6
|
|
51
|
+
with:
|
|
52
|
+
python-version: ${{ matrix.python-version }}
|
|
53
|
+
- run: uv sync --group dev
|
|
54
|
+
- name: Wait for MongoDB
|
|
55
|
+
run: |
|
|
56
|
+
for i in $(seq 1 30); do
|
|
57
|
+
uv run python -c "import socket; socket.create_connection(('127.0.0.1', 27017), 1).close()" && exit 0
|
|
58
|
+
sleep 1
|
|
59
|
+
done
|
|
60
|
+
echo "MongoDB did not become ready" >&2
|
|
61
|
+
exit 1
|
|
62
|
+
- run: uv run pytest tests/ -q --cov=icelake --cov-report=xml
|
|
63
|
+
- uses: codecov/codecov-action@v5
|
|
64
|
+
if: matrix.python-version == '3.12'
|
|
65
|
+
with:
|
|
66
|
+
files: coverage.xml
|
|
67
|
+
fail_ci_if_error: false
|
|
68
|
+
|
|
69
|
+
changelog:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
if: github.event_name == 'pull_request'
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
with:
|
|
75
|
+
fetch-depth: 0
|
|
76
|
+
- uses: astral-sh/setup-uv@v6
|
|
77
|
+
with:
|
|
78
|
+
python-version: "3.12"
|
|
79
|
+
- run: uv sync --group dev
|
|
80
|
+
- run: uv run towncrier check --compare-with origin/${{ github.base_ref }}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
7
|
+
- "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
|
|
8
|
+
- "v[0-9]+.[0-9]+.[0-9]+[ab][0-9]+"
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v6
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Test
|
|
22
|
+
run: uv sync --group dev && uv run pytest tests/ -q
|
|
23
|
+
- name: Build
|
|
24
|
+
run: uv build
|
|
25
|
+
- uses: actions/upload-artifact@v4
|
|
26
|
+
with:
|
|
27
|
+
name: dist
|
|
28
|
+
path: dist/
|
|
29
|
+
|
|
30
|
+
publish:
|
|
31
|
+
needs: build
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
environment:
|
|
34
|
+
name: pypi
|
|
35
|
+
url: https://pypi.org/p/icelake
|
|
36
|
+
permissions:
|
|
37
|
+
id-token: write
|
|
38
|
+
steps:
|
|
39
|
+
- uses: astral-sh/setup-uv@v6
|
|
40
|
+
- uses: actions/download-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
- run: uv publish
|
|
45
|
+
|
|
46
|
+
release:
|
|
47
|
+
needs: publish
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
permissions:
|
|
50
|
+
contents: write
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
- name: Extract release notes
|
|
54
|
+
id: notes
|
|
55
|
+
run: |
|
|
56
|
+
TAG="${GITHUB_REF_NAME}"
|
|
57
|
+
VERSION="${TAG#v}"
|
|
58
|
+
awk -v v="$VERSION" '
|
|
59
|
+
$0 ~ "^## \\[" v "\\]" { found=1; next }
|
|
60
|
+
found && /^## \[/ { exit }
|
|
61
|
+
found { print }
|
|
62
|
+
' CHANGELOG.md > release_notes.md
|
|
63
|
+
if [ ! -s release_notes.md ]; then
|
|
64
|
+
echo "See [CHANGELOG.md](CHANGELOG.md) for details." > release_notes.md
|
|
65
|
+
fi
|
|
66
|
+
case "$VERSION" in
|
|
67
|
+
*a[0-9]*|*b[0-9]*|*rc[0-9]*) echo "prerelease=true" >> "$GITHUB_OUTPUT" ;;
|
|
68
|
+
*) echo "prerelease=false" >> "$GITHUB_OUTPUT" ;;
|
|
69
|
+
esac
|
|
70
|
+
- uses: softprops/action-gh-release@v2
|
|
71
|
+
with:
|
|
72
|
+
body_path: release_notes.md
|
|
73
|
+
prerelease: ${{ steps.notes.outputs.prerelease }}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
bump:
|
|
7
|
+
description: "Base bump. 'none' releases the version already in pyproject.toml (e.g. a manual major)."
|
|
8
|
+
required: true
|
|
9
|
+
type: choice
|
|
10
|
+
options: [minor, patch, none]
|
|
11
|
+
prerelease:
|
|
12
|
+
description: "Pre-release stage (PEP 440). Pre-releases may run on any branch and push only a tag."
|
|
13
|
+
required: true
|
|
14
|
+
type: choice
|
|
15
|
+
options: [none, alpha, beta, rc]
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
actions: write
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
release:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
concurrency: release
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
- uses: astral-sh/setup-uv@v6
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
- run: uv sync --group dev
|
|
33
|
+
|
|
34
|
+
- name: Guard stable releases to main
|
|
35
|
+
if: inputs.prerelease == 'none'
|
|
36
|
+
run: |
|
|
37
|
+
if [ "${{ github.ref_name }}" != "main" ]; then
|
|
38
|
+
echo "Stable releases must run on main. Use a prerelease stage from a feature branch." >&2
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
- name: Compute version
|
|
43
|
+
id: bump
|
|
44
|
+
run: |
|
|
45
|
+
set -euo pipefail
|
|
46
|
+
if [ "${{ inputs.bump }}" = "none" ]; then
|
|
47
|
+
BASE="$(uv version --short)"
|
|
48
|
+
else
|
|
49
|
+
BASE="$(uv version --bump "${{ inputs.bump }}" --dry-run | awk '{print $NF}')"
|
|
50
|
+
fi
|
|
51
|
+
if [ "${{ inputs.prerelease }}" = "none" ]; then
|
|
52
|
+
VERSION="$BASE"
|
|
53
|
+
else
|
|
54
|
+
case "${{ inputs.prerelease }}" in
|
|
55
|
+
alpha) SHORT=a ;; beta) SHORT=b ;; rc) SHORT=rc ;;
|
|
56
|
+
esac
|
|
57
|
+
LAST=0
|
|
58
|
+
for TAG in $(git tag -l "v${BASE}${SHORT}[0-9]*"); do
|
|
59
|
+
N="${TAG#v${BASE}${SHORT}}"
|
|
60
|
+
[ "$N" -gt "$LAST" ] && LAST="$N"
|
|
61
|
+
done
|
|
62
|
+
VERSION="${BASE}${SHORT}$((LAST + 1))"
|
|
63
|
+
fi
|
|
64
|
+
if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then
|
|
65
|
+
echo "Tag v${VERSION} already exists." >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
uv version "$VERSION" --no-sync
|
|
69
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
70
|
+
|
|
71
|
+
- name: Build changelog
|
|
72
|
+
run: uv run towncrier build --yes --version "${{ steps.bump.outputs.version }}"
|
|
73
|
+
|
|
74
|
+
- name: Commit and tag
|
|
75
|
+
run: |
|
|
76
|
+
git config user.name "github-actions[bot]"
|
|
77
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
78
|
+
git add pyproject.toml uv.lock CHANGELOG.md changelog.d
|
|
79
|
+
git commit -m "release: v${{ steps.bump.outputs.version }}"
|
|
80
|
+
git tag -a "v${{ steps.bump.outputs.version }}" -m "v${{ steps.bump.outputs.version }}"
|
|
81
|
+
|
|
82
|
+
- name: Push stable release to main
|
|
83
|
+
if: inputs.prerelease == 'none'
|
|
84
|
+
run: git push origin HEAD:main
|
|
85
|
+
|
|
86
|
+
- name: Push tag
|
|
87
|
+
run: git push origin "v${{ steps.bump.outputs.version }}"
|
|
88
|
+
|
|
89
|
+
- name: Trigger PyPI publish
|
|
90
|
+
env:
|
|
91
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
92
|
+
run: |
|
|
93
|
+
# GITHUB_TOKEN tag pushes do not start other workflows; dispatch explicitly.
|
|
94
|
+
# Retry briefly in case the tag is not yet visible as a dispatch ref.
|
|
95
|
+
for i in 1 2 3 4 5; do
|
|
96
|
+
gh workflow run publish.yml --ref "v${{ steps.bump.outputs.version }}" && exit 0
|
|
97
|
+
sleep 2
|
|
98
|
+
done
|
|
99
|
+
echo "Failed to dispatch publish.yml for v${{ steps.bump.outputs.version }}" >&2
|
|
100
|
+
exit 1
|
icelake-0.5.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
icelake-0.5.0/AGENTS.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Core Principles (Ordered - Higher Wins on Conflict)
|
|
2
|
+
|
|
3
|
+
## Code is a liability, not an asset
|
|
4
|
+
|
|
5
|
+
- Before writing code, answer: (1) Does a dependency or existing module already do
|
|
6
|
+
this? (2) What breaks upstream? (3) What depends on this downstream?
|
|
7
|
+
- Equal designs -> fewer lines wins. No speculative abstractions, no "just in case"
|
|
8
|
+
helpers, no unused parameters.
|
|
9
|
+
- Deleting code is a feature. When a module loses its justification, say so.
|
|
10
|
+
- Never add code to appear thorough. Deliberate, minimal additions only.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Adopt proven machinery; never rebuild it
|
|
14
|
+
|
|
15
|
+
- If a mature, maintained dependency solves the problem, use it — don't hand-roll
|
|
16
|
+
a weaker version. Check the project's architecture decision records / plan docs
|
|
17
|
+
for what has been adopted and what is explicitly out of scope.
|
|
18
|
+
- Conversely: if a dependency is deprecated or unmaintained, flag it rather than
|
|
19
|
+
building deeper on top of it.
|
|
20
|
+
|
|
21
|
+
## One concern per module
|
|
22
|
+
|
|
23
|
+
- Each module owns one concern and exposes it through a narrow interface.
|
|
24
|
+
Reaching into another module's internals is a violation — use its contract.
|
|
25
|
+
|
|
26
|
+
## Design for the swap you can't predict
|
|
27
|
+
|
|
28
|
+
- Every external dependency (database, LLM provider, library) is treated as
|
|
29
|
+
replaceable. The test: "if we swapped X tomorrow, how many files change?"
|
|
30
|
+
If the answer is more than the adapter plus config, the abstraction has leaked.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Code Quality Standards
|
|
34
|
+
|
|
35
|
+
## Baseline
|
|
36
|
+
|
|
37
|
+
- Full type annotations on all public interfaces; strict type-checker clean.
|
|
38
|
+
- Linter + formatter clean before merge; no suppressions without a comment
|
|
39
|
+
explaining why.
|
|
40
|
+
- All data crossing a boundary (API I/O, persistence, inter-process, LLM I/O)
|
|
41
|
+
is validated by a schema. No raw dicts at boundaries.
|
|
42
|
+
- Fixed sets of values are enums, never raw strings.
|
|
43
|
+
|
|
44
|
+
## Clarity
|
|
45
|
+
|
|
46
|
+
- Names state intent and units (`timeout_seconds`, not `timeout`).
|
|
47
|
+
- Public capability entry points carry user-facing docstrings — treat them as
|
|
48
|
+
product surface, not developer notes.
|
|
49
|
+
- Async on all I/O paths; never block the event loop.
|
|
50
|
+
|
|
51
|
+
## Size gates (need explicit justification to exceed)
|
|
52
|
+
|
|
53
|
+
- Function: ~40 lines. Module: ~300 lines. Justification means a comment or PR
|
|
54
|
+
note explaining why splitting would be worse.
|
|
55
|
+
|
|
56
|
+
## Testing
|
|
57
|
+
|
|
58
|
+
- Business logic must be testable without transport, network, or live services —
|
|
59
|
+
inject fakes at the seams. Core correctness logic (validation, verification,
|
|
60
|
+
state transitions) gets exhaustive tests.
|
|
61
|
+
- Test behavior at interfaces, not implementation details.
|
|
62
|
+
|
|
63
|
+
## Every change passes this checklist
|
|
64
|
+
|
|
65
|
+
1. Does something that exists already do this?
|
|
66
|
+
2. What breaks upstream? What depends on this downstream?
|
|
67
|
+
3. Can this be fewer lines?
|
|
68
|
+
4. Is this logic duplicated elsewhere (or does it belong in a shared layer)?
|
|
69
|
+
5. Are new boundaries schema-validated?
|
|
70
|
+
6. Are new costs (API calls, storage growth, latency) metered and bounded?
|
|
71
|
+
7. Does this work with N processes, or only one?
|
|
72
|
+
|
|
73
|
+
## Hygiene
|
|
74
|
+
|
|
75
|
+
- No commented-out code (git is the history). No TODOs without a linked issue.
|
|
76
|
+
- No dead config knobs, no unused exports.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to icelake are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versions follow
|
|
5
|
+
[PEP 440](https://peps.python.org/pep-0440/).
|
|
6
|
+
|
|
7
|
+
Release notes are assembled from fragments in `changelog.d/` with
|
|
8
|
+
[towncrier](https://towncrier.readthedocs.io/). To add an entry, drop a
|
|
9
|
+
`<issue-or-name>.<type>.md` fragment into `changelog.d/` where `<type>` is one
|
|
10
|
+
of `security`, `removed`, `deprecated`, `added`, `changed`, `fixed`.
|
|
11
|
+
|
|
12
|
+
<!-- towncrier release notes start -->
|
|
13
|
+
|
|
14
|
+
## [0.5.0](https://github.com/nulzo/icelake/tree/v0.5.0) - 2026-09-07
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
- Removed the tag-echo citation path: `Citations.parse`, `MarkerMode`, `ParsedReply`, `UsedSource`, `Citations.instructions`/`source_list`, `PromptContext.resolve_used`/`apply_citations`, `UsedCitation`, and `render_citation_tag`. The injection block no longer carries `[mem:N]` tags, and no regex parses model output — citation-shaped prose the model writes is inert text, while links only ever come from the closed set via attribution or provider-anchored offsets. Migrate: build the set with `Citations.from_prompt_context(ctx)`, call `await memory.attribute_citations(reply, set_, guild_id=...)`, then `set_.apply(reply, attribution)`. ([#citation-tags](https://github.com/nulzo/icelake/issues/citation-tags))
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Citations are now grounded **post-generation** via structured attribution, replacing model-echoed tags entirely. The answer model sees plain facts (no `[mem:N]` tags, no URLs, no citation contract); after generation, `DiscordMemory.attribute_citations()` maps verbatim reply spans to the closed citation set in one cheap structured call on the small-model tier, and `Citations.apply(text, attribution)` weaves `[[N]](<url>)` jump links at the verified spans. Only claims a registered source actually supports get cited — retrieved-but-unused sources never render, and source-list dumps are structurally impossible. Added `icelake.attribution` (`attribute`, `ReplyAttribution`, `AttributedClaim`), `Citations.citable`, `fact_source_urls`, and a `MeterPurpose.ATTRIBUTION` meter key. ([#citation-attribution](https://github.com/nulzo/icelake/issues/citation-attribution))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## [0.4.3](https://github.com/nulzo/icelake/tree/v0.4.3) - 2026-09-07
|
|
26
|
+
|
|
27
|
+
No significant changes.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [0.4.2](https://github.com/nulzo/icelake/tree/v0.4.2) - 2026-09-07
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Added `icelake.citations`: a reply-side citation system built on a closed source set with deny-by-default parsing, mirroring how production assistants (ChatGPT, Claude, Perplexity) keep citations reliable. `Citations` seeds from `PromptContext`, registers link sources (`add_source`) and Discord messages (`add_message`) with library-minted `[src:N]` refs, exposes the model-facing `instructions` / `source_list`, and validates with one call — `parse(text, markers=...)` splices provider-anchored offsets, resolves echoed refs, canonicalizes mangled-but-known references, and deletes every citation-shaped token outside the set (invented `[src:9]`, self-written `[1](url)` links, `【…】` artifacts). `parse` returns a structured `ParsedReply` (cleaned `text` + resolved `citations`) so presentation — inline links, footers, or stripped markers — is the consumer's; an `apply(text)` convenience weaves Discord jump links for the common bot path. Also added `MarkerMode`, `CitationSource`, `UsedSource`, `render_fact_list()`, and top-level exports of `Citations`, `MarkerMode`, `ParsedReply`, `UsedSource`, `CitationSource`, `message_url`, and `render_fact_list`. ([#citation-registry](https://github.com/nulzo/icelake/issues/citation-registry))
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- `PromptContext.apply_citations` now delegates to `Citations.apply` — a single parsing implementation for all citation handling. Behavior tightens deny-by-default: model-written `[N](url)` links whose target is not a registered source and `【…】` artifacts are now removed, while word-attached prose like `array[0]` is preserved. ([#apply-citations-registry](https://github.com/nulzo/icelake/issues/apply-citations-registry))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## [0.4.1](https://github.com/nulzo/icelake/tree/v0.4.1) - 2026-09-07
|
|
42
|
+
|
|
43
|
+
No significant changes.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## [0.4.0](https://github.com/nulzo/icelake/tree/v0.4.0) - 2026-09-07
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- Closed-ID citation objects plus an optional OpenRouter L2 reranker after RRF.
|
|
51
|
+
The generator may only echo advertised `[mem:N]` tags (or structured `fact_id`
|
|
52
|
+
claims); consumers call `PromptContext.resolve_used` and weave the returned
|
|
53
|
+
objects. Banter emits no IDs, so `used` is empty and no links are inserted.
|
|
54
|
+
Rerank degrades to hybrid order on failure, timeout, or score-arity mismatch.
|
|
55
|
+
`prompt_context` now pair-intersects every combination of mentions and thread
|
|
56
|
+
participants and enables graph-hop recall whenever anyone else is in the turn.
|
|
57
|
+
`graph.shared_attributions` returns both members' stances over shared entities
|
|
58
|
+
so consumers can show agreement and disagreement without a second fetch. ([#citation-objects-reranker](https://github.com/nulzo/icelake/issues/citation-objects-reranker))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## [0.3.4](https://github.com/nulzo/icelake/tree/v0.3.4) - 2026-09-06
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Relation endpoints named after a guild member now collapse to that member's user
|
|
66
|
+
node at write time (no more person-entity twins), and every person-facing graph
|
|
67
|
+
query (`relations_of`, `between`, `shared`, `similar_users`, `entity_stances`,
|
|
68
|
+
`neighbors`) follows `entity.linked_user_id` so pre-existing twins still resolve
|
|
69
|
+
to the member. Reads go through a single seam (`GraphApi._incident`) that unions
|
|
70
|
+
the member's user node with every entity twin and collapses the result, so edges
|
|
71
|
+
stored against a twin (a member mentioned by name before they spoke) surface on
|
|
72
|
+
that member's star. Added `graph.shared(a, b)`: the identity-collapsed,
|
|
73
|
+
weight-ranked intersection of two members' entity edges, with polarity
|
|
74
|
+
preserved. Bot IDs are no longer written to `FactRecord.related_user_ids`. ([#identity-collapse](https://github.com/nulzo/icelake/issues/identity-collapse))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## [0.3.3](https://github.com/nulzo/icelake/tree/v0.3.3) - 2026-08-31
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- Documented the extraction-model benchmark in the README: ranked OpenRouter
|
|
82
|
+
models on the e2e sim (quality, spend, latency) with a fixed weighted score
|
|
83
|
+
so later runs can be appended without rescaling. Includes the 2026-08-30
|
|
84
|
+
challenger round (GPT-4o-mini, GPT-4.1-mini, Gemini 2.5 Flash, Qwen3 32B). ([#model-bench-readme](https://github.com/nulzo/icelake/issues/model-bench-readme))
|
|
85
|
+
- Documented the name-in-prose lookup pattern ("what do you know about X?" with no
|
|
86
|
+
@mention): a new README section explains that `prompt_context` is mention-keyed by
|
|
87
|
+
design and shows the resolve-then-strict-fetch flow (`identity.resolve` →
|
|
88
|
+
`facts.list_for_subject`, never guessing on ambiguity) across three entry points —
|
|
89
|
+
slash command (zero LLM), a structured-output router, and native function calling.
|
|
90
|
+
Added `examples/name_lookup_tool.py`, a runnable LLM-free demo of the shared
|
|
91
|
+
handler, and wired the pattern into `examples/omni_style_bot.py` (router +
|
|
92
|
+
`/memory lookup`). ([#name-lookup-docs](https://github.com/nulzo/icelake/issues/name-lookup-docs))
|
|
93
|
+
- `RetrievalConfig.top_k` and `RetrievalConfig.max_per_subject` now bound the
|
|
94
|
+
`prompt_context` hot path (previously hardcoded to the `RecallQuery` defaults
|
|
95
|
+
of 8 total / 4 per subject), so consumers can widen per-turn memory injection
|
|
96
|
+
from configuration. ([#prompt-context-caps](https://github.com/nulzo/icelake/issues/prompt-context-caps))
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- Bench matrix is a per-model OpenRouter param map (reasoning, temperature,
|
|
101
|
+
structured_outputs) instead of one global flag. LlmConfig accepts
|
|
102
|
+
`reasoning=none` to disable thinking on models that allow it. ([#bench-model-params](https://github.com/nulzo/icelake/issues/bench-model-params))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## [0.3.2](https://github.com/nulzo/icelake/tree/v0.3.2) - 2026-08-30
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
|
|
109
|
+
- Added `lifecycle.decay_stability_days` (default 7.0), the Ebbinghaus timescale:
|
|
110
|
+
retention is now exp(-days / (strength x stability)), so a one-off fact survives
|
|
111
|
+
~3 weeks instead of ~3 days. Forgetting policy moved into a single pure selector
|
|
112
|
+
(`lifecycle.select_forgotten_facts`) shared by all store adapters — mongo/sqlite
|
|
113
|
+
no longer carry private copies of the decay math, and the sweep is one bulk
|
|
114
|
+
UPDATE instead of per-fact writes. ([#decay-stability](https://github.com/nulzo/icelake/issues/decay-stability))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
## [0.3.1](https://github.com/nulzo/icelake/tree/v0.3.1) - 2026-08-29
|
|
118
|
+
|
|
119
|
+
No significant changes.
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
## [0.3.0](https://github.com/nulzo/icelake/tree/v0.3.0) - 2026-08-29
|
|
123
|
+
|
|
124
|
+
No significant changes.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## [0.2.0](https://github.com/nulzo/icelake/tree/v0.2.0) - 2026-08-29
|
|
128
|
+
|
|
129
|
+
No significant changes.
|
icelake-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nolan Gregory
|
|
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.
|