File size: 2,808 Bytes
210ef30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Parsing the memory extractor's answer, and deciding which scope a fact lands in.

Blueprint 6.9 and master prompt 4.5. The asymmetry is the whole design:

* A fact extracted from a **Normal-mode** conversation is always ``shared``. Nothing said in a
  room where others can read the screen is private by construction.
* A fact from **GF mode** is whatever the extractor says — and when the answer is malformed,
  missing a scope, or anything other than a clean ``shared``, it defaults to ``private``.

Leaking a private fact into Normal mode is far worse than failing to surface a harmless one, so
every uncertainty resolves toward ``private``.
"""

from dataclasses import dataclass

from ..persona import prova
from ..persona.types import Mode, Scope

#: Longest a stored fact may be. The extractor is told "one short sentence"; anything much
#: longer is the model narrating the conversation back, which is not a fact.
MAX_FACT_CHARS = 280


@dataclass(frozen=True)
class ExtractedFact:
    scope: Scope
    content: str


def parse_extraction(raw: str, mode: Mode) -> ExtractedFact | None:
    """``None`` means there is nothing worth remembering."""
    text = raw.strip().strip('"').strip()
    if not text or text.upper().rstrip(".") == prova.MEMORY_EXTRACTION_NONE:
        return None

    # Only the first line counts. A chatty model that adds "Hope that helps!" underneath has not
    # changed the fact.
    line = text.splitlines()[0].strip()
    if line.upper().rstrip(".") == prova.MEMORY_EXTRACTION_NONE:
        return None

    scope_text, separator, fact = line.partition("|")
    if not separator:
        # No scope at all. The whole line is the fact and the scope is unknown.
        fact, scope = line, Scope.PRIVATE
    else:
        scope = _scope_from(scope_text)

    fact = fact.strip().strip('"').strip()
    if not fact or len(fact) > MAX_FACT_CHARS:
        return None
    if fact.upper().rstrip(".") == prova.MEMORY_EXTRACTION_NONE:
        return None

    # Normal-mode talk is never private, whatever the extractor thought.
    if mode is Mode.NORMAL:
        scope = Scope.SHARED

    return ExtractedFact(scope=scope, content=fact)


def _scope_from(text: str) -> Scope:
    """Only an exact ``shared`` is shared. Everything else — including typos — is private."""
    return Scope.SHARED if text.strip().lower() == "shared" else Scope.PRIVATE


def forced_scope(requested: str | None, mode: Mode) -> Scope:
    """The scope for a fact he asked her to remember via the ``remember`` tool.

    Blueprint 8.1: "In Normal mode, scope is forced to shared."
    """
    if mode is Mode.NORMAL:
        return Scope.SHARED
    if requested and requested.strip().lower() == "shared":
        return Scope.SHARED
    return Scope.PRIVATE if requested else Scope.SHARED