cpu-llm 1.7.0__py3-none-any.whl
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.
- cpu_llm/__init__.py +3 -0
- cpu_llm/agents/__init__.py +5 -0
- cpu_llm/agents/deep_search.py +67 -0
- cpu_llm/agents/discovery.py +138 -0
- cpu_llm/agents/gather.py +184 -0
- cpu_llm/agents/imagine.py +149 -0
- cpu_llm/agents/reply.py +334 -0
- cpu_llm/agents/summarize.py +85 -0
- cpu_llm/assembly.py +93 -0
- cpu_llm/audit/__init__.py +3 -0
- cpu_llm/audit/entailment.py +110 -0
- cpu_llm/catalog.py +144 -0
- cpu_llm/channels.py +409 -0
- cpu_llm/checkpoints/vectorprism.pt +0 -0
- cpu_llm/checkpoints/vectorprism.pt.json +31 -0
- cpu_llm/classifier.py +143 -0
- cpu_llm/clauses.py +138 -0
- cpu_llm/cognition/__init__.py +13 -0
- cpu_llm/cognition/adapter.py +416 -0
- cpu_llm/comparison.py +153 -0
- cpu_llm/consolidation/__init__.py +5 -0
- cpu_llm/consolidation/sleep.py +357 -0
- cpu_llm/cortex_sleep.py +211 -0
- cpu_llm/cpu_guard.py +19 -0
- cpu_llm/data/artificial-intelligence.md +139 -0
- cpu_llm/data/wikitext-2-raw-v1/PROVENANCE.txt +17 -0
- cpu_llm/data/wikitext-2-raw-v1/test-PROVENANCE.txt +21 -0
- cpu_llm/data/wikitext-2-raw-v1/test.parquet +0 -0
- cpu_llm/data/wikitext-2-raw-v1/validation.parquet +0 -0
- cpu_llm/documents.py +301 -0
- cpu_llm/geometry/__init__.py +19 -0
- cpu_llm/geometry/poincare.py +144 -0
- cpu_llm/grounding.py +109 -0
- cpu_llm/ingest.py +89 -0
- cpu_llm/ir/__init__.py +41 -0
- cpu_llm/ir/schema.py +240 -0
- cpu_llm/manifest_spans.py +83 -0
- cpu_llm/measurements.py +480 -0
- cpu_llm/pipeline.py +1746 -0
- cpu_llm/proposition_catalog.py +40 -0
- cpu_llm/realizer/__init__.py +3 -0
- cpu_llm/realizer/inflect.py +95 -0
- cpu_llm/realizer/surface.py +232 -0
- cpu_llm/relations.py +310 -0
- cpu_llm/retrieval.py +264 -0
- cpu_llm/source.py +113 -0
- cpu_llm/stack.py +67 -0
- cpu_llm/taxonomy.py +95 -0
- cpu_llm/thinker_pool.py +395 -0
- cpu_llm/word_taxonomy.py +325 -0
- cpu_llm-1.7.0.dist-info/METADATA +368 -0
- cpu_llm-1.7.0.dist-info/RECORD +55 -0
- cpu_llm-1.7.0.dist-info/WHEEL +5 -0
- cpu_llm-1.7.0.dist-info/entry_points.txt +2 -0
- cpu_llm-1.7.0.dist-info/top_level.txt +1 -0
cpu_llm/__init__.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Every chunk a deep-search prompt can reach, with none removed.
|
|
2
|
+
|
|
3
|
+
The seeds are the prompt's content words, not the words that asked for the
|
|
4
|
+
search. A seed keeps every paragraph stored under it, in every article. The
|
|
5
|
+
relation walk then keeps every paragraph of the words one, two, and three
|
|
6
|
+
edges further. The checked sentence is not replaced.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from cpu_llm.catalog import article_slug
|
|
14
|
+
from cpu_llm.channels import _content_words
|
|
15
|
+
from cpu_llm.relations import relation_hands
|
|
16
|
+
|
|
17
|
+
_NOT_SEEDS = frozenset(
|
|
18
|
+
"deep search deeply everywhere possible possibilities dive look all".split()
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def collect_possibilities(
|
|
23
|
+
message: str,
|
|
24
|
+
word_tags: dict[str, dict[str, list[str]]],
|
|
25
|
+
edges: list[Any] | None = None,
|
|
26
|
+
) -> dict[str, Any]:
|
|
27
|
+
"""Return every paragraph the seeds and their relation hands name."""
|
|
28
|
+
seeds = [word for word in _content_words(message) if word not in _NOT_SEEDS]
|
|
29
|
+
found: dict[str, dict[str, Any]] = {}
|
|
30
|
+
for seed in seeds:
|
|
31
|
+
_add_word(found, word_tags, seed, "word")
|
|
32
|
+
hands = {1: [], 2: [], 3: []}
|
|
33
|
+
if edges and seeds:
|
|
34
|
+
hands = relation_hands(edges, seeds, limit=100_000)
|
|
35
|
+
for hand, words in hands.items():
|
|
36
|
+
for word in words:
|
|
37
|
+
_add_word(found, word_tags, word, f"hand-{hand}")
|
|
38
|
+
possibilities = [found[document_id] for document_id in sorted(found)]
|
|
39
|
+
return {
|
|
40
|
+
"seeds": seeds,
|
|
41
|
+
"count": len(possibilities),
|
|
42
|
+
"hands": {str(hand): list(words) for hand, words in hands.items()},
|
|
43
|
+
"possibilities": possibilities,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _add_word(
|
|
48
|
+
found: dict[str, dict[str, Any]],
|
|
49
|
+
word_tags: dict[str, dict[str, list[str]]],
|
|
50
|
+
word: str,
|
|
51
|
+
via: str,
|
|
52
|
+
) -> None:
|
|
53
|
+
for document_ids in (word_tags.get(word) or {}).values():
|
|
54
|
+
for document_id in document_ids:
|
|
55
|
+
item = found.setdefault(
|
|
56
|
+
document_id,
|
|
57
|
+
{
|
|
58
|
+
"document_id": document_id,
|
|
59
|
+
"article": article_slug(document_id),
|
|
60
|
+
"matched_words": [],
|
|
61
|
+
"via": [],
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
if word not in item["matched_words"]:
|
|
65
|
+
item["matched_words"].append(word)
|
|
66
|
+
if via not in item["via"]:
|
|
67
|
+
item["via"].append(via)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Discovery Loop, stages 1 through 4.
|
|
2
|
+
|
|
3
|
+
The agent records what the turn already established. It does not add a
|
|
4
|
+
conclusion, and it does not call a model. Stage 5 was not in the protocol
|
|
5
|
+
text that defined this agent, so the loop stops before scale shifting.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
PROTOCOL_VERSION = "1.0"
|
|
14
|
+
INVARIANT = "Reality and evidence have the final veto."
|
|
15
|
+
|
|
16
|
+
_PERSPECTIVES: dict[str, tuple[str, ...]] = {
|
|
17
|
+
"individual_humans": ("human", "person", "patient", "individual"),
|
|
18
|
+
"groups": ("society", "group", "community"),
|
|
19
|
+
"institutions": ("institution", "government", "agency"),
|
|
20
|
+
"artificial_agents": ("agent",),
|
|
21
|
+
"animals": ("animal", "species"),
|
|
22
|
+
"ecosystems": ("ecosystem", "ecology"),
|
|
23
|
+
"engineered_systems": ("machine", "engine", "device"),
|
|
24
|
+
"planetary": ("planet", "cosmic", "climate"),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def run_discovery(state: dict[str, Any]) -> dict[str, Any]:
|
|
29
|
+
claim = state.get("message") or ""
|
|
30
|
+
related = list(state.get("related") or [])
|
|
31
|
+
questions = [str(item) for item in state.get("gather_questions") or []]
|
|
32
|
+
evidence_text = " ".join(item.get("chunk_text") or "" for item in related)
|
|
33
|
+
return {
|
|
34
|
+
"protocol": "discovery-loop",
|
|
35
|
+
"version": PROTOCOL_VERSION,
|
|
36
|
+
"invariant": INVARIANT,
|
|
37
|
+
"stages_completed": [1, 2, 3, 4],
|
|
38
|
+
"stopped_before": "Stage 5: Scale Shifting",
|
|
39
|
+
"claim": _claim(claim, related, state),
|
|
40
|
+
"assumptions": _assumptions(state),
|
|
41
|
+
"information": _information(claim, related, questions, state),
|
|
42
|
+
"perspectives": _perspectives(f"{claim} {evidence_text}"),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _claim(claim: str, related: list[dict[str, Any]], state: dict[str, Any]) -> dict[str, Any]:
|
|
47
|
+
return {
|
|
48
|
+
"text": claim,
|
|
49
|
+
"true_if": [
|
|
50
|
+
"A proposition paired with the retrieved tensor is realized and the auditor accepts the sentence.",
|
|
51
|
+
"Every number in that sentence matches a PrismManifest evidence span.",
|
|
52
|
+
],
|
|
53
|
+
"false_if": [
|
|
54
|
+
"The prompt is blocked.",
|
|
55
|
+
"Deliberation reports a contradiction.",
|
|
56
|
+
"The sentence contains a number that is not an evidence span.",
|
|
57
|
+
"PrismThinker returns refuse.",
|
|
58
|
+
],
|
|
59
|
+
"scope": {
|
|
60
|
+
"tenant_id": state.get("tenant_id") or "cpu-llm",
|
|
61
|
+
"evidence_ids": [item.get("chunk_id") for item in related],
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _assumptions(state: dict[str, Any]) -> list[dict[str, str]]:
|
|
67
|
+
items = [
|
|
68
|
+
{
|
|
69
|
+
"kind": "technical",
|
|
70
|
+
"text": "Retrieval rank is topical relevance. It is not authority.",
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
if state.get("cognition_status") == "needs_evidence_propositions":
|
|
74
|
+
items.append(
|
|
75
|
+
{
|
|
76
|
+
"kind": "logical",
|
|
77
|
+
"text": "A retrieved chunk is not a proposition, and it is not treated as one.",
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
if state.get("grammar_span_ids"):
|
|
81
|
+
items.append(
|
|
82
|
+
{
|
|
83
|
+
"kind": "mathematical",
|
|
84
|
+
"text": "A number in the sentence is allowed only when its canonical value matches an evidence span.",
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
if state.get("thinker_directive") == "gather":
|
|
88
|
+
items.append(
|
|
89
|
+
{
|
|
90
|
+
"kind": "empirical",
|
|
91
|
+
"text": "An unresolved gather question is missing information, not an observation.",
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
return items
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _information(
|
|
98
|
+
claim: str,
|
|
99
|
+
related: list[dict[str, Any]],
|
|
100
|
+
questions: list[str],
|
|
101
|
+
state: dict[str, Any],
|
|
102
|
+
) -> list[dict[str, str]]:
|
|
103
|
+
rows: list[dict[str, str]] = [
|
|
104
|
+
{"kind": "hypothesis", "text": claim, "provenance": "user"},
|
|
105
|
+
]
|
|
106
|
+
for item in related:
|
|
107
|
+
rows.append(
|
|
108
|
+
{
|
|
109
|
+
"kind": "reported",
|
|
110
|
+
"text": item.get("chunk_text") or "",
|
|
111
|
+
"provenance": str(item.get("chunk_id") or "vectorprism"),
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
status = state.get("cognition_status")
|
|
115
|
+
if status == "deliberated" and state.get("abstention_triggered"):
|
|
116
|
+
rows.append(
|
|
117
|
+
{
|
|
118
|
+
"kind": "unknown",
|
|
119
|
+
"text": state.get("answer") or "",
|
|
120
|
+
"provenance": "deliberation",
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
elif status == "deliberated" and state.get("answer"):
|
|
124
|
+
kind = "interpretation" if float(state.get("ruin_distance") or 0) == 0 else "unknown"
|
|
125
|
+
rows.append({"kind": kind, "text": state.get("answer") or "", "provenance": "realizer"})
|
|
126
|
+
for question in questions:
|
|
127
|
+
rows.append({"kind": "unknown", "text": question, "provenance": "prismthinker"})
|
|
128
|
+
return rows
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _perspectives(text: str) -> list[dict[str, str]]:
|
|
132
|
+
folded = text.casefold()
|
|
133
|
+
selected: list[dict[str, str]] = []
|
|
134
|
+
for name, words in _PERSPECTIVES.items():
|
|
135
|
+
hits = [word for word in words if re.search(rf"\b{re.escape(word)}\b", folded)]
|
|
136
|
+
if hits:
|
|
137
|
+
selected.append({"perspective": name, "named_in_evidence": ", ".join(hits)})
|
|
138
|
+
return selected
|
cpu_llm/agents/gather.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Gather every paragraph of the creative article and rank its words.
|
|
2
|
+
|
|
3
|
+
The shortlist names the article. This node loads the rest of that article,
|
|
4
|
+
not only the lead paragraph. It collects the content words of those
|
|
5
|
+
paragraphs and ranks them by how often they sit in a paragraph that shares
|
|
6
|
+
the prompt's words. The checked sentence is not written here.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import random
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from cpu_llm.catalog import article_slug
|
|
17
|
+
from cpu_llm.channels import _content_words, _cosine, _slice
|
|
18
|
+
from cpu_llm.word_taxonomy import category_number
|
|
19
|
+
from vectorprism.tensor_contract import PSMTensorContract
|
|
20
|
+
|
|
21
|
+
_MOST = 32
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def realize_ranked_paragraphs(
|
|
25
|
+
message: str,
|
|
26
|
+
rows: list[dict[str, Any]],
|
|
27
|
+
pool: dict[str, Any],
|
|
28
|
+
proposition_for,
|
|
29
|
+
realizer,
|
|
30
|
+
auditor,
|
|
31
|
+
*,
|
|
32
|
+
numbers_allowed=None,
|
|
33
|
+
vary: bool = False,
|
|
34
|
+
rng: random.Random | None = None,
|
|
35
|
+
avoid: set[str] | None = None,
|
|
36
|
+
) -> dict[str, Any]:
|
|
37
|
+
"""Realize the ranked paragraphs with the sentence function, then check each one.
|
|
38
|
+
|
|
39
|
+
A paragraph is skipped when it has no proposition the realizer can repeat,
|
|
40
|
+
when the auditor rejects the sentence, or when a number in it is not in
|
|
41
|
+
the gathered paragraphs. The checked answer is left alone. With ``vary``,
|
|
42
|
+
each call draws a different order and a different subset of the sentences
|
|
43
|
+
that passed those checks.
|
|
44
|
+
"""
|
|
45
|
+
by_id = {str(row["document_id"]): row for row in rows}
|
|
46
|
+
chosen = [item for item in pool.get("paragraphs") or [] if item.get("relevance", 0) > 0]
|
|
47
|
+
if not chosen:
|
|
48
|
+
chosen = list(pool.get("paragraphs") or [])[:1]
|
|
49
|
+
links: list[dict[str, Any]] = []
|
|
50
|
+
sentences: list[str] = []
|
|
51
|
+
for item in chosen:
|
|
52
|
+
document_id = str(item["document_id"])
|
|
53
|
+
row = by_id.get(document_id)
|
|
54
|
+
if row is None:
|
|
55
|
+
continue
|
|
56
|
+
text = str(row.get("chunk_text") or "")
|
|
57
|
+
proposition = proposition_for(document_id, text, row.get("tensor_1024d"))
|
|
58
|
+
if proposition is None:
|
|
59
|
+
continue
|
|
60
|
+
sentence, delivered = realizer.synthesize([proposition], [])
|
|
61
|
+
if not sentence or not auditor.audit_delivered_output(delivered, [proposition]):
|
|
62
|
+
continue
|
|
63
|
+
if numbers_allowed is not None and not numbers_allowed(sentence):
|
|
64
|
+
continue
|
|
65
|
+
prompt = set(_content_words(message))
|
|
66
|
+
keywords = [word for word in _content_words(sentence) if word in prompt]
|
|
67
|
+
links.append(
|
|
68
|
+
{
|
|
69
|
+
"chunk_id": document_id,
|
|
70
|
+
"sentence": sentence,
|
|
71
|
+
"keywords": keywords,
|
|
72
|
+
"relevance": item.get("relevance", 0),
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
if vary:
|
|
76
|
+
links = _varied_passage(links, rng or random.SystemRandom(), set(avoid or ()))
|
|
77
|
+
sentences = [item["sentence"] for item in links]
|
|
78
|
+
return {
|
|
79
|
+
"text": " ".join(sentences),
|
|
80
|
+
"links": links,
|
|
81
|
+
"realized": len(sentences),
|
|
82
|
+
"varied": vary,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _varied_passage(
|
|
87
|
+
links: list[dict[str, Any]],
|
|
88
|
+
rng: random.Random,
|
|
89
|
+
avoid: set[str],
|
|
90
|
+
) -> list[dict[str, Any]]:
|
|
91
|
+
"""Pick an arrangement that is not one already returned for this prompt."""
|
|
92
|
+
if len(links) < 2:
|
|
93
|
+
return links
|
|
94
|
+
limit = min(len(links), 3)
|
|
95
|
+
for _ in range(48):
|
|
96
|
+
count = rng.randint(1, limit)
|
|
97
|
+
picks = rng.sample(list(links), count)
|
|
98
|
+
if _passage(picks) not in avoid:
|
|
99
|
+
return picks
|
|
100
|
+
for shift in range(len(links)):
|
|
101
|
+
rotated = links[shift:] + links[:shift]
|
|
102
|
+
for count in range(1, limit + 1):
|
|
103
|
+
picks = rotated[:count]
|
|
104
|
+
if _passage(picks) not in avoid:
|
|
105
|
+
return picks
|
|
106
|
+
return links
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _passage(links: list[dict[str, Any]]) -> str:
|
|
110
|
+
return " ".join(item["sentence"] for item in links)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def gather_article(
|
|
114
|
+
message: str,
|
|
115
|
+
chunks: list[dict[str, Any]],
|
|
116
|
+
query: np.ndarray | None = None,
|
|
117
|
+
) -> dict[str, Any]:
|
|
118
|
+
"""Rank the words and paragraphs in ``chunks`` against ``message``.
|
|
119
|
+
|
|
120
|
+
``chunks`` carry ``document_id`` and ``chunk_text``. A ``tensor`` is used
|
|
121
|
+
only as a dense-core tie break. Every chunk stays in the group.
|
|
122
|
+
"""
|
|
123
|
+
prompt = list(dict.fromkeys(_content_words(message)))
|
|
124
|
+
prompt_set = set(prompt)
|
|
125
|
+
articles: list[str] = []
|
|
126
|
+
paragraphs: list[dict[str, Any]] = []
|
|
127
|
+
locations: dict[str, list[str]] = {}
|
|
128
|
+
for chunk in chunks:
|
|
129
|
+
document_id = str(chunk["document_id"])
|
|
130
|
+
slug = article_slug(document_id)
|
|
131
|
+
if slug not in articles:
|
|
132
|
+
articles.append(slug)
|
|
133
|
+
words = list(dict.fromkeys(_content_words(str(chunk.get("chunk_text") or ""))))
|
|
134
|
+
present = set(words)
|
|
135
|
+
shared = [word for word in prompt if word in present]
|
|
136
|
+
cosine = _dense_cosine(query, chunk.get("tensor") or chunk.get("tensor_1024d"))
|
|
137
|
+
paragraphs.append(
|
|
138
|
+
{
|
|
139
|
+
"document_id": document_id,
|
|
140
|
+
"article": slug,
|
|
141
|
+
"relevance": len(shared),
|
|
142
|
+
"prompt_words": shared,
|
|
143
|
+
"cosine": cosine,
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
for word in words:
|
|
147
|
+
locations.setdefault(word, [])
|
|
148
|
+
if document_id not in locations[word]:
|
|
149
|
+
locations[word].append(document_id)
|
|
150
|
+
supporting = {item["document_id"] for item in paragraphs if item["relevance"] > 0}
|
|
151
|
+
if not supporting:
|
|
152
|
+
supporting = {item["document_id"] for item in paragraphs}
|
|
153
|
+
ranked_words = []
|
|
154
|
+
for word, document_ids in locations.items():
|
|
155
|
+
support = sum(1 for document_id in document_ids if document_id in supporting)
|
|
156
|
+
score = support + (2 if word in prompt_set else 0)
|
|
157
|
+
ranked_words.append((score, word, document_ids))
|
|
158
|
+
ranked_words.sort(key=lambda item: (-item[0], item[1]))
|
|
159
|
+
paragraphs.sort(
|
|
160
|
+
key=lambda item: (
|
|
161
|
+
-item["relevance"],
|
|
162
|
+
-(item["cosine"] if item["cosine"] is not None else -2.0),
|
|
163
|
+
item["document_id"],
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
return {
|
|
167
|
+
"articles": articles,
|
|
168
|
+
"categories": {slug: category_number(slug) for slug in articles},
|
|
169
|
+
"paragraph_count": len(paragraphs),
|
|
170
|
+
"word_count": len(ranked_words),
|
|
171
|
+
"paragraphs": paragraphs,
|
|
172
|
+
"most_relevant": [
|
|
173
|
+
{"word": word, "score": score, "paragraphs": len(document_ids)}
|
|
174
|
+
for score, word, document_ids in ranked_words[:_MOST]
|
|
175
|
+
],
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _dense_cosine(query: np.ndarray | None, tensor: Any) -> float | None:
|
|
180
|
+
if query is None or tensor is None:
|
|
181
|
+
return None
|
|
182
|
+
left = _slice(np.asarray(query, dtype=np.float64).reshape(-1), PSMTensorContract.DENSE_CORE)
|
|
183
|
+
right = _slice(np.asarray(tensor, dtype=np.float64).reshape(-1), PSMTensorContract.DENSE_CORE)
|
|
184
|
+
return _cosine(left, right)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""An imagined passage from one article's stored tensors.
|
|
2
|
+
|
|
3
|
+
The shortlist names the articles. Every paragraph of those articles is loaded.
|
|
4
|
+
PrismResonance scores each paragraph against the question. A paragraph whose
|
|
5
|
+
interference is not positive is left out. The paragraphs that stay are ordered
|
|
6
|
+
by a walk along their dense cores: each step is the nearest unused paragraph.
|
|
7
|
+
The sentence copied from a paragraph is one already written there. The words
|
|
8
|
+
named on a step are the question's content words that occur in that sentence.
|
|
9
|
+
|
|
10
|
+
This is not a cause, a measurement, or a proposition, and it is not the
|
|
11
|
+
checked sentence.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
from vectorprism.tensor_contract import PSMTensorContract
|
|
20
|
+
|
|
21
|
+
from cpu_llm.catalog import article_slug
|
|
22
|
+
from cpu_llm.channels import _best_sentence, _content_words, _cosine, _slice
|
|
23
|
+
|
|
24
|
+
_NOTE = (
|
|
25
|
+
"Each sentence is copied from a paragraph of an article the shortlist already named. "
|
|
26
|
+
"PrismResonance keeps a paragraph whose interference with the question is positive. "
|
|
27
|
+
"Dense-core cosine orders those paragraphs. The link is that the tensors sit near each other."
|
|
28
|
+
)
|
|
29
|
+
_EMPTY = "The shortlist named no article, so there is nothing to connect."
|
|
30
|
+
_SILENT = "None of the loaded paragraphs interfered with the question."
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def compose_imagination(
|
|
34
|
+
message: str,
|
|
35
|
+
chunks: list[dict[str, Any]],
|
|
36
|
+
resonance: dict[str, float],
|
|
37
|
+
) -> dict[str, Any]:
|
|
38
|
+
"""Walk ``chunks`` whose resonance scores are positive.
|
|
39
|
+
|
|
40
|
+
``chunks`` carry ``document_id``, ``chunk_text``, and ``tensor``.
|
|
41
|
+
``resonance`` maps a document id to the PrismResonance score.
|
|
42
|
+
"""
|
|
43
|
+
if not chunks:
|
|
44
|
+
return _result([], [], _EMPTY)
|
|
45
|
+
kept: list[dict[str, Any]] = []
|
|
46
|
+
for chunk in chunks:
|
|
47
|
+
document_id = str(chunk["document_id"])
|
|
48
|
+
score = resonance.get(document_id)
|
|
49
|
+
if score is None or not np.isfinite(score) or float(score) <= 0.0:
|
|
50
|
+
continue
|
|
51
|
+
tensor = np.asarray(chunk["tensor"], dtype=np.float64).reshape(-1)
|
|
52
|
+
kept.append(
|
|
53
|
+
{
|
|
54
|
+
"document_id": document_id,
|
|
55
|
+
"chunk_text": str(chunk["chunk_text"]),
|
|
56
|
+
"tensor": tensor,
|
|
57
|
+
"resonance": float(score),
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
if not kept:
|
|
61
|
+
return _result(_articles(chunks), [], _SILENT, loaded=len(chunks))
|
|
62
|
+
ordered = _walk(kept)
|
|
63
|
+
links: list[dict[str, Any]] = []
|
|
64
|
+
keywords: list[str] = []
|
|
65
|
+
sentences: list[str] = []
|
|
66
|
+
for item in ordered:
|
|
67
|
+
sentence = _best_sentence(item["chunk_text"], message)
|
|
68
|
+
words = _shared_keywords(message, sentence)
|
|
69
|
+
for word in words:
|
|
70
|
+
if word not in keywords:
|
|
71
|
+
keywords.append(word)
|
|
72
|
+
cosine = item["cosine_to_previous"]
|
|
73
|
+
links.append(
|
|
74
|
+
{
|
|
75
|
+
"chunk_id": item["document_id"],
|
|
76
|
+
"resonance": item["resonance"],
|
|
77
|
+
"cosine_to_previous": None if cosine is None else float(cosine),
|
|
78
|
+
"sentence": sentence,
|
|
79
|
+
"keywords": words,
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
if sentence:
|
|
83
|
+
sentences.append(sentence)
|
|
84
|
+
return _result(_articles(chunks), links, _NOTE, loaded=len(chunks), keywords=keywords, text=" ".join(sentences))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _result(
|
|
88
|
+
articles: list[str],
|
|
89
|
+
links: list[dict[str, Any]],
|
|
90
|
+
note: str,
|
|
91
|
+
*,
|
|
92
|
+
loaded: int = 0,
|
|
93
|
+
keywords: list[str] | None = None,
|
|
94
|
+
text: str = "",
|
|
95
|
+
) -> dict[str, Any]:
|
|
96
|
+
return {
|
|
97
|
+
"triggered": True,
|
|
98
|
+
"articles": articles,
|
|
99
|
+
"loaded": loaded,
|
|
100
|
+
"links": links,
|
|
101
|
+
"keywords": list(keywords or []),
|
|
102
|
+
"text": text,
|
|
103
|
+
"note": note,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _articles(chunks: list[dict[str, Any]]) -> list[str]:
|
|
108
|
+
slugs: list[str] = []
|
|
109
|
+
for chunk in chunks:
|
|
110
|
+
slug = article_slug(str(chunk["document_id"]))
|
|
111
|
+
if slug not in slugs:
|
|
112
|
+
slugs.append(slug)
|
|
113
|
+
return slugs
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _shared_keywords(message: str, sentence: str) -> list[str]:
|
|
117
|
+
present = set(_content_words(sentence))
|
|
118
|
+
words: list[str] = []
|
|
119
|
+
for word in _content_words(message):
|
|
120
|
+
if word in present and word not in words:
|
|
121
|
+
words.append(word)
|
|
122
|
+
return words
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _dense(tensor: np.ndarray) -> np.ndarray:
|
|
126
|
+
return _slice(tensor, PSMTensorContract.DENSE_CORE)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _walk(kept: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
130
|
+
remaining = sorted(kept, key=lambda item: (-item["resonance"], item["document_id"]))
|
|
131
|
+
start = remaining.pop(0)
|
|
132
|
+
start["cosine_to_previous"] = None
|
|
133
|
+
ordered = [start]
|
|
134
|
+
while remaining:
|
|
135
|
+
current = _dense(ordered[-1]["tensor"])
|
|
136
|
+
best_at = 0
|
|
137
|
+
best_rank: tuple[float, str] | None = None
|
|
138
|
+
best_cosine = None
|
|
139
|
+
for index, item in enumerate(remaining):
|
|
140
|
+
cosine = _cosine(current, _dense(item["tensor"]))
|
|
141
|
+
rank = (-(cosine if cosine is not None else -2.0), item["document_id"])
|
|
142
|
+
if best_rank is None or rank < best_rank:
|
|
143
|
+
best_rank = rank
|
|
144
|
+
best_at = index
|
|
145
|
+
best_cosine = cosine
|
|
146
|
+
chosen = remaining.pop(best_at)
|
|
147
|
+
chosen["cosine_to_previous"] = best_cosine
|
|
148
|
+
ordered.append(chosen)
|
|
149
|
+
return ordered
|