Improve chat experience and begin search implementation

This commit is contained in:
2026-02-15 17:32:26 +00:00
parent 6612274ab9
commit a94bbff655
21 changed files with 3081 additions and 179 deletions

View File

@@ -155,6 +155,55 @@ INSIGHT_METRICS = {
"values often precede misunderstandings or withdrawal cycles."
),
},
"reciprocity_score": {
"title": "Reciprocity Component",
"group": "stability",
"history_field": "reciprocity_score",
"calculation": (
"100 * (1 - |inbound - outbound| / total_messages). Higher means "
"more balanced participation."
),
"psychology": (
"Lower reciprocity can reflect perceived asymmetry and rising pursuit/"
"withdraw cycles."
),
},
"continuity_score": {
"title": "Continuity Component",
"group": "stability",
"history_field": "continuity_score",
"calculation": (
"100 * min(1, distinct_sample_days / span_days). Higher means steadier "
"day-to-day continuity."
),
"psychology": (
"Drops can signal communication becoming episodic or reactive."
),
},
"response_score": {
"title": "Response Component",
"group": "stability",
"history_field": "response_score",
"calculation": (
"Average of inbound and outbound response-lag scores, each mapped from "
"median lag to a 0-100 curve."
),
"psychology": (
"Lower response score can indicate delayed repair loops during tension."
),
},
"volatility_score": {
"title": "Volatility Component",
"group": "stability",
"history_field": "volatility_score",
"calculation": (
"Derived from coefficient of variation of daily message counts and "
"inverted to a 0-100 stability signal."
),
"psychology": (
"High volatility can suggest inconsistent rhythm and reduced predictability."
),
},
"stability_confidence": {
"title": "Stability Confidence",
"group": "confidence",
@@ -219,6 +268,52 @@ INSIGHT_METRICS = {
"Estimates user follow-through and consistency toward the counterpart."
),
},
"inbound_response_score": {
"title": "Inbound Response Score",
"group": "commitment",
"history_field": "inbound_response_score",
"calculation": (
"Response-speed score built from median lag between user outbound and "
"counterpart inbound replies."
),
"psychology": (
"Lower values suggest delayed reciprocity from counterpart direction."
),
},
"outbound_response_score": {
"title": "Outbound Response Score",
"group": "commitment",
"history_field": "outbound_response_score",
"calculation": (
"Response-speed score built from median lag between counterpart inbound "
"and user outbound replies."
),
"psychology": "Lower values suggest slower follow-through from user direction.",
},
"balance_inbound_score": {
"title": "Inbound Balance Score",
"group": "commitment",
"history_field": "balance_inbound_score",
"calculation": (
"100 * min(1, inbound_messages / outbound_messages). Captures inbound "
"participation parity."
),
"psychology": (
"Lower values can indicate one-sided conversational load from user side."
),
},
"balance_outbound_score": {
"title": "Outbound Balance Score",
"group": "commitment",
"history_field": "balance_outbound_score",
"calculation": (
"100 * min(1, outbound_messages / inbound_messages). Captures outbound "
"participation parity."
),
"psychology": (
"Lower values can indicate one-sided conversational load from counterpart side."
),
},
"commitment_confidence": {
"title": "Commit Confidence",
"group": "confidence",
@@ -334,6 +429,78 @@ INSIGHT_GRAPH_SPECS = [
"y_min": 0,
"y_max": None,
},
{
"slug": "reciprocity_score",
"title": "Reciprocity Component",
"field": "reciprocity_score",
"group": "stability",
"y_min": 0,
"y_max": 100,
},
{
"slug": "continuity_score",
"title": "Continuity Component",
"field": "continuity_score",
"group": "stability",
"y_min": 0,
"y_max": 100,
},
{
"slug": "response_score",
"title": "Response Component",
"field": "response_score",
"group": "stability",
"y_min": 0,
"y_max": 100,
},
{
"slug": "volatility_score",
"title": "Volatility Component",
"field": "volatility_score",
"group": "stability",
"y_min": 0,
"y_max": 100,
},
{
"slug": "inbound_response_score",
"title": "Inbound Response Score",
"field": "inbound_response_score",
"group": "commitment",
"y_min": 0,
"y_max": 100,
},
{
"slug": "outbound_response_score",
"title": "Outbound Response Score",
"field": "outbound_response_score",
"group": "commitment",
"y_min": 0,
"y_max": 100,
},
{
"slug": "balance_inbound_score",
"title": "Inbound Balance Score",
"field": "balance_inbound_score",
"group": "commitment",
"y_min": 0,
"y_max": 100,
},
{
"slug": "balance_outbound_score",
"title": "Outbound Balance Score",
"field": "balance_outbound_score",
"group": "commitment",
"y_min": 0,
"y_max": 100,
},
{
"slug": "last_event",
"title": "Last Event Timestamp",
"field": "source_event_ts",
"group": "timeline",
"y_min": None,
"y_max": None,
},
]
@@ -487,7 +654,10 @@ def _to_float(value):
return float(value)
def _format_metric_value(conversation, metric_slug):
def _format_metric_value(conversation, metric_slug, latest_snapshot=None):
snapshot = latest_snapshot
if snapshot is None:
snapshot = conversation.metric_snapshots.first()
if metric_slug == "platform":
return conversation.get_platform_type_display() or "-"
if metric_slug == "thread":
@@ -498,6 +668,14 @@ def _format_metric_value(conversation, metric_slug):
return conversation.get_stability_state_display()
if metric_slug == "stability_score":
return conversation.stability_score
if metric_slug == "reciprocity_score":
return snapshot.reciprocity_score if snapshot else None
if metric_slug == "continuity_score":
return snapshot.continuity_score if snapshot else None
if metric_slug == "response_score":
return snapshot.response_score if snapshot else None
if metric_slug == "volatility_score":
return snapshot.volatility_score if snapshot else None
if metric_slug == "stability_confidence":
return conversation.stability_confidence
if metric_slug == "sample_messages":
@@ -510,6 +688,14 @@ def _format_metric_value(conversation, metric_slug):
return conversation.commitment_inbound_score
if metric_slug == "commitment_outbound":
return conversation.commitment_outbound_score
if metric_slug == "inbound_response_score":
return snapshot.inbound_response_score if snapshot else None
if metric_slug == "outbound_response_score":
return snapshot.outbound_response_score if snapshot else None
if metric_slug == "balance_inbound_score":
return snapshot.balance_inbound_score if snapshot else None
if metric_slug == "balance_outbound_score":
return snapshot.balance_outbound_score if snapshot else None
if metric_slug == "commitment_confidence":
return conversation.commitment_confidence
if metric_slug == "commitment_computed":
@@ -2713,7 +2899,8 @@ class AIWorkspaceInsightDetail(LoginRequiredMixin, View):
person = get_object_or_404(Person, pk=person_id, user=request.user)
conversation = _conversation_for_person(request.user, person)
value = _format_metric_value(conversation, metric)
latest_snapshot = conversation.metric_snapshots.first()
value = _format_metric_value(conversation, metric, latest_snapshot)
group = INSIGHT_GROUPS[spec["group"]]
points = []
if spec["history_field"]:
@@ -2773,6 +2960,7 @@ class AIWorkspaceInsightHelp(LoginRequiredMixin, View):
person = get_object_or_404(Person, pk=person_id, user=request.user)
conversation = _conversation_for_person(request.user, person)
latest_snapshot = conversation.metric_snapshots.first()
metrics = []
for slug, spec in INSIGHT_METRICS.items():
metrics.append(
@@ -2783,7 +2971,11 @@ class AIWorkspaceInsightHelp(LoginRequiredMixin, View):
"group_title": INSIGHT_GROUPS[spec["group"]]["title"],
"calculation": spec["calculation"],
"psychology": spec["psychology"],
"value": _format_metric_value(conversation, slug),
"value": _format_metric_value(
conversation,
slug,
latest_snapshot,
),
}
)