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.
Files changed (176) hide show
  1. icelake-0.5.0/.github/workflows/ci.yml +80 -0
  2. icelake-0.5.0/.github/workflows/publish.yml +73 -0
  3. icelake-0.5.0/.github/workflows/release.yml +100 -0
  4. icelake-0.5.0/.gitignore +24 -0
  5. icelake-0.5.0/.python-version +1 -0
  6. icelake-0.5.0/AGENTS.md +76 -0
  7. icelake-0.5.0/CHANGELOG.md +129 -0
  8. icelake-0.5.0/LICENSE +21 -0
  9. icelake-0.5.0/PKG-INFO +698 -0
  10. icelake-0.5.0/README.md +665 -0
  11. icelake-0.5.0/changelog.d/.gitignore +1 -0
  12. icelake-0.5.0/evals/golden/adversarial_injection.yaml +8 -0
  13. icelake-0.5.0/evals/golden/ambiguous_identity.yaml +14 -0
  14. icelake-0.5.0/evals/golden/bare_link_skip.yaml +7 -0
  15. icelake-0.5.0/evals/golden/contradiction_supersede.yaml +14 -0
  16. icelake-0.5.0/evals/golden/cross_user_isolation.yaml +21 -0
  17. icelake-0.5.0/evals/golden/durable_preference.yaml +14 -0
  18. icelake-0.5.0/evals/golden/mention_extraction.yaml +18 -0
  19. icelake-0.5.0/evals/golden/reinforce_not_duplicate.yaml +15 -0
  20. icelake-0.5.0/evals/golden/server_culture.yaml +14 -0
  21. icelake-0.5.0/evals/golden/snowflake_poisoning.yaml +7 -0
  22. icelake-0.5.0/evals/golden/third_party_attribution.yaml +18 -0
  23. icelake-0.5.0/evals/golden/transient_state_skip.yaml +9 -0
  24. icelake-0.5.0/evals/golden/write_create_biographical.yaml +14 -0
  25. icelake-0.5.0/evals/golden/write_skip_noise.yaml +9 -0
  26. icelake-0.5.0/evals/golden/write_skip_questions.yaml +9 -0
  27. icelake-0.5.0/evals/golden_runner.py +176 -0
  28. icelake-0.5.0/examples/bench_models.py +264 -0
  29. icelake-0.5.0/examples/e2e_simulation.py +1444 -0
  30. icelake-0.5.0/examples/name_lookup_tool.py +226 -0
  31. icelake-0.5.0/examples/omni_style_bot.py +543 -0
  32. icelake-0.5.0/examples/ping_reply_bot.py +291 -0
  33. icelake-0.5.0/examples/relationship_queries.py +290 -0
  34. icelake-0.5.0/pyproject.toml +118 -0
  35. icelake-0.5.0/src/icelake/__init__.py +249 -0
  36. icelake-0.5.0/src/icelake/_json.py +125 -0
  37. icelake-0.5.0/src/icelake/adapters/__init__.py +1 -0
  38. icelake-0.5.0/src/icelake/adapters/embedders/__init__.py +126 -0
  39. icelake-0.5.0/src/icelake/adapters/embedders/cached.py +74 -0
  40. icelake-0.5.0/src/icelake/adapters/embedders/local.py +51 -0
  41. icelake-0.5.0/src/icelake/adapters/in_memory/__init__.py +10 -0
  42. icelake-0.5.0/src/icelake/adapters/in_memory/queue.py +245 -0
  43. icelake-0.5.0/src/icelake/adapters/in_memory/store.py +930 -0
  44. icelake-0.5.0/src/icelake/adapters/in_memory/vectors.py +58 -0
  45. icelake-0.5.0/src/icelake/adapters/llm_cache.py +51 -0
  46. icelake-0.5.0/src/icelake/adapters/llm_openai_compat.py +235 -0
  47. icelake-0.5.0/src/icelake/adapters/llm_openrouter.py +41 -0
  48. icelake-0.5.0/src/icelake/adapters/meter.py +167 -0
  49. icelake-0.5.0/src/icelake/adapters/mongo/__init__.py +1272 -0
  50. icelake-0.5.0/src/icelake/adapters/mongo/mapping.py +268 -0
  51. icelake-0.5.0/src/icelake/adapters/mongo/queue.py +300 -0
  52. icelake-0.5.0/src/icelake/adapters/mongo/vectors.py +109 -0
  53. icelake-0.5.0/src/icelake/adapters/rerankers.py +121 -0
  54. icelake-0.5.0/src/icelake/adapters/sqlite/connection.py +385 -0
  55. icelake-0.5.0/src/icelake/adapters/sqlite/llm_cache.py +40 -0
  56. icelake-0.5.0/src/icelake/adapters/sqlite/queue.py +315 -0
  57. icelake-0.5.0/src/icelake/adapters/sqlite/store.py +68 -0
  58. icelake-0.5.0/src/icelake/adapters/sqlite/store_facts.py +857 -0
  59. icelake-0.5.0/src/icelake/adapters/sqlite/store_graph.py +590 -0
  60. icelake-0.5.0/src/icelake/adapters/sqlite/vectors.py +120 -0
  61. icelake-0.5.0/src/icelake/api/classify.py +88 -0
  62. icelake-0.5.0/src/icelake/api/client.py +879 -0
  63. icelake-0.5.0/src/icelake/api/events.py +73 -0
  64. icelake-0.5.0/src/icelake/api/facts_api.py +352 -0
  65. icelake-0.5.0/src/icelake/api/groups.py +382 -0
  66. icelake-0.5.0/src/icelake/attribution.py +155 -0
  67. icelake-0.5.0/src/icelake/citations.py +250 -0
  68. icelake-0.5.0/src/icelake/config.py +395 -0
  69. icelake-0.5.0/src/icelake/consolidation/service.py +158 -0
  70. icelake-0.5.0/src/icelake/errors.py +60 -0
  71. icelake-0.5.0/src/icelake/graph/__init__.py +5 -0
  72. icelake-0.5.0/src/icelake/graph/collapse.py +54 -0
  73. icelake-0.5.0/src/icelake/graph/relations.py +87 -0
  74. icelake-0.5.0/src/icelake/graph/traversal.py +102 -0
  75. icelake-0.5.0/src/icelake/graph/writes.py +234 -0
  76. icelake-0.5.0/src/icelake/identity/__init__.py +6 -0
  77. icelake-0.5.0/src/icelake/identity/aliases.py +169 -0
  78. icelake-0.5.0/src/icelake/identity/guards.py +63 -0
  79. icelake-0.5.0/src/icelake/identity/resolver.py +109 -0
  80. icelake-0.5.0/src/icelake/ids.py +20 -0
  81. icelake-0.5.0/src/icelake/ingest/__init__.py +5 -0
  82. icelake-0.5.0/src/icelake/ingest/context_builder.py +63 -0
  83. icelake-0.5.0/src/icelake/ingest/executor.py +314 -0
  84. icelake-0.5.0/src/icelake/ingest/extraction.py +142 -0
  85. icelake-0.5.0/src/icelake/ingest/gates.py +186 -0
  86. icelake-0.5.0/src/icelake/ingest/pipeline.py +791 -0
  87. icelake-0.5.0/src/icelake/ingest/reconcile.py +271 -0
  88. icelake-0.5.0/src/icelake/ingest/roster.py +98 -0
  89. icelake-0.5.0/src/icelake/integrations/__init__.py +5 -0
  90. icelake-0.5.0/src/icelake/integrations/discord_py.py +233 -0
  91. icelake-0.5.0/src/icelake/lifecycle/__init__.py +15 -0
  92. icelake-0.5.0/src/icelake/lifecycle/forget.py +48 -0
  93. icelake-0.5.0/src/icelake/lifecycle/maintenance.py +92 -0
  94. icelake-0.5.0/src/icelake/lifecycle/prune.py +50 -0
  95. icelake-0.5.0/src/icelake/lifecycle/strength.py +62 -0
  96. icelake-0.5.0/src/icelake/lifecycle/tiers.py +86 -0
  97. icelake-0.5.0/src/icelake/models/__init__.py +129 -0
  98. icelake-0.5.0/src/icelake/models/admin.py +116 -0
  99. icelake-0.5.0/src/icelake/models/common.py +46 -0
  100. icelake-0.5.0/src/icelake/models/events.py +129 -0
  101. icelake-0.5.0/src/icelake/models/facts.py +187 -0
  102. icelake-0.5.0/src/icelake/models/graph.py +158 -0
  103. icelake-0.5.0/src/icelake/models/identity.py +75 -0
  104. icelake-0.5.0/src/icelake/models/operations.py +112 -0
  105. icelake-0.5.0/src/icelake/models/retrieval.py +190 -0
  106. icelake-0.5.0/src/icelake/ports/__init__.py +48 -0
  107. icelake-0.5.0/src/icelake/ports/clock.py +51 -0
  108. icelake-0.5.0/src/icelake/ports/llm.py +124 -0
  109. icelake-0.5.0/src/icelake/ports/queue.py +133 -0
  110. icelake-0.5.0/src/icelake/ports/store.py +425 -0
  111. icelake-0.5.0/src/icelake/ports/vectors.py +64 -0
  112. icelake-0.5.0/src/icelake/prompts/__init__.py +1 -0
  113. icelake-0.5.0/src/icelake/prompts/extraction.py +106 -0
  114. icelake-0.5.0/src/icelake/py.typed +0 -0
  115. icelake-0.5.0/src/icelake/retrieval/__init__.py +5 -0
  116. icelake-0.5.0/src/icelake/retrieval/channels.py +223 -0
  117. icelake-0.5.0/src/icelake/retrieval/injection.py +196 -0
  118. icelake-0.5.0/src/icelake/retrieval/service.py +432 -0
  119. icelake-0.5.0/src/icelake/scoring/__init__.py +5 -0
  120. icelake-0.5.0/src/icelake/scoring/fusion.py +103 -0
  121. icelake-0.5.0/src/icelake/structured.py +76 -0
  122. icelake-0.5.0/src/icelake/visualizer/__init__.py +18 -0
  123. icelake-0.5.0/src/icelake/visualizer/__main__.py +124 -0
  124. icelake-0.5.0/src/icelake/visualizer/html.py +693 -0
  125. icelake-0.5.0/src/icelake/visualizer/models.py +98 -0
  126. icelake-0.5.0/src/icelake/visualizer/snapshot.py +331 -0
  127. icelake-0.5.0/tests/__init__.py +0 -0
  128. icelake-0.5.0/tests/conftest.py +160 -0
  129. icelake-0.5.0/tests/integration/__init__.py +0 -0
  130. icelake-0.5.0/tests/integration/test_adversarial.py +350 -0
  131. icelake-0.5.0/tests/integration/test_agentic_e2e.py +464 -0
  132. icelake-0.5.0/tests/integration/test_api_groups.py +139 -0
  133. icelake-0.5.0/tests/integration/test_coverage_backends.py +309 -0
  134. icelake-0.5.0/tests/integration/test_decay_loop.py +109 -0
  135. icelake-0.5.0/tests/integration/test_discord_integration.py +225 -0
  136. icelake-0.5.0/tests/integration/test_examples.py +214 -0
  137. icelake-0.5.0/tests/integration/test_golden_evals.py +18 -0
  138. icelake-0.5.0/tests/integration/test_graph_recall.py +122 -0
  139. icelake-0.5.0/tests/integration/test_hardening_round.py +614 -0
  140. icelake-0.5.0/tests/integration/test_import_export.py +58 -0
  141. icelake-0.5.0/tests/integration/test_mongo_live.py +208 -0
  142. icelake-0.5.0/tests/integration/test_mongo_vectors.py +74 -0
  143. icelake-0.5.0/tests/integration/test_phase0.py +293 -0
  144. icelake-0.5.0/tests/integration/test_pipeline_e2e.py +802 -0
  145. icelake-0.5.0/tests/integration/test_queue_conformance.py +126 -0
  146. icelake-0.5.0/tests/integration/test_recall_prompt.py +316 -0
  147. icelake-0.5.0/tests/integration/test_review_round.py +533 -0
  148. icelake-0.5.0/tests/integration/test_store_conformance.py +537 -0
  149. icelake-0.5.0/tests/unit/__init__.py +0 -0
  150. icelake-0.5.0/tests/unit/test_adapters.py +477 -0
  151. icelake-0.5.0/tests/unit/test_adapters_edge.py +224 -0
  152. icelake-0.5.0/tests/unit/test_citations.py +254 -0
  153. icelake-0.5.0/tests/unit/test_config.py +197 -0
  154. icelake-0.5.0/tests/unit/test_context_builder.py +81 -0
  155. icelake-0.5.0/tests/unit/test_coverage_completion.py +525 -0
  156. icelake-0.5.0/tests/unit/test_coverage_final.py +465 -0
  157. icelake-0.5.0/tests/unit/test_coverage_gaps.py +181 -0
  158. icelake-0.5.0/tests/unit/test_executor.py +313 -0
  159. icelake-0.5.0/tests/unit/test_extraction.py +296 -0
  160. icelake-0.5.0/tests/unit/test_gates.py +111 -0
  161. icelake-0.5.0/tests/unit/test_identity.py +167 -0
  162. icelake-0.5.0/tests/unit/test_identity_collapse.py +324 -0
  163. icelake-0.5.0/tests/unit/test_ingest_debug.py +92 -0
  164. icelake-0.5.0/tests/unit/test_injection_roster.py +248 -0
  165. icelake-0.5.0/tests/unit/test_lifecycle.py +303 -0
  166. icelake-0.5.0/tests/unit/test_models.py +123 -0
  167. icelake-0.5.0/tests/unit/test_mongo_adapter.py +176 -0
  168. icelake-0.5.0/tests/unit/test_reconcile.py +253 -0
  169. icelake-0.5.0/tests/unit/test_reranker_citations.py +324 -0
  170. icelake-0.5.0/tests/unit/test_scoring_graph.py +187 -0
  171. icelake-0.5.0/tests/unit/test_structured.py +56 -0
  172. icelake-0.5.0/tests/unit/test_structured_outputs.py +182 -0
  173. icelake-0.5.0/tests/unit/test_transactions.py +122 -0
  174. icelake-0.5.0/tests/unit/test_vectors.py +62 -0
  175. icelake-0.5.0/tests/unit/test_visualizer.py +366 -0
  176. 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
@@ -0,0 +1,24 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ *.db
13
+ *.db-shm
14
+ *.db-wal
15
+
16
+ .coverage
17
+
18
+ .bench/
19
+
20
+ .env
21
+
22
+ bench_runs/
23
+
24
+ docs/
@@ -0,0 +1 @@
1
+ 3.14
@@ -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.