spec: add BEHAVIOR_SPEC and fix B-01~B-10 (resolved/decay/scoring)
- Add BEHAVIOR_SPEC.md as full system behaviour reference - B-01: stop auto-archiving resolved buckets in update() - B-03: keep activation_count as float in calculate_score - B-04: initialise activation_count=0 on create - B-05: time score coefficient 0.1 -> 0.02 - B-06: w_time default 2.5 -> 1.5 - B-07: content_weight default 3.0 -> 1.0 - B-08: refresh local meta after auto_resolve - B-09: user-supplied valence/arousal takes priority over analyze() - B-10: allow empty domain for feel buckets - Refresh INTERNALS/README/dashboard accordingly
This commit is contained in:
@@ -39,6 +39,8 @@ def _parse_claude_json(data: dict | list) -> list[dict]:
|
||||
turns = []
|
||||
conversations = data if isinstance(data, list) else [data]
|
||||
for conv in conversations:
|
||||
if not isinstance(conv, dict):
|
||||
continue
|
||||
messages = conv.get("chat_messages", conv.get("messages", []))
|
||||
for msg in messages:
|
||||
if not isinstance(msg, dict):
|
||||
@@ -61,18 +63,27 @@ def _parse_chatgpt_json(data: list | dict) -> list[dict]:
|
||||
turns = []
|
||||
conversations = data if isinstance(data, list) else [data]
|
||||
for conv in conversations:
|
||||
if not isinstance(conv, dict):
|
||||
continue
|
||||
mapping = conv.get("mapping", {})
|
||||
if mapping:
|
||||
# ChatGPT uses a tree structure with mapping
|
||||
sorted_nodes = sorted(
|
||||
mapping.values(),
|
||||
key=lambda n: n.get("message", {}).get("create_time", 0) or 0,
|
||||
)
|
||||
# Filter out None nodes before sorting
|
||||
valid_nodes = [n for n in mapping.values() if isinstance(n, dict)]
|
||||
|
||||
def _node_ts(n):
|
||||
msg = n.get("message")
|
||||
if not isinstance(msg, dict):
|
||||
return 0
|
||||
return msg.get("create_time") or 0
|
||||
|
||||
sorted_nodes = sorted(valid_nodes, key=_node_ts)
|
||||
for node in sorted_nodes:
|
||||
msg = node.get("message")
|
||||
if not msg or not isinstance(msg, dict):
|
||||
continue
|
||||
content_parts = msg.get("content", {}).get("parts", [])
|
||||
content_obj = msg.get("content", {})
|
||||
content_parts = content_obj.get("parts", []) if isinstance(content_obj, dict) else []
|
||||
content = " ".join(str(p) for p in content_parts if p)
|
||||
if not content.strip():
|
||||
continue
|
||||
@@ -168,7 +179,7 @@ def detect_and_parse(raw_content: str, filename: str = "") -> list[dict]:
|
||||
# Single conversation object with role/content messages
|
||||
if "role" in sample and "content" in sample:
|
||||
return _parse_claude_json(data)
|
||||
except (json.JSONDecodeError, KeyError, IndexError):
|
||||
except (json.JSONDecodeError, KeyError, IndexError, AttributeError, TypeError):
|
||||
pass
|
||||
|
||||
# Fall back to markdown/text
|
||||
|
||||
Reference in New Issue
Block a user