hindsight-api 0.0.17__py3-none-any.whl → 0.0.20__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.
@@ -94,7 +94,7 @@ class MemoryEngine:
94
94
  - Embedding generation for semantic search
95
95
  - Entity, temporal, and semantic link creation
96
96
  - Think operations for formulating answers with opinions
97
- - bank profile and personality management
97
+ - bank profile and disposition management
98
98
  """
99
99
 
100
100
  def __init__(
@@ -676,7 +676,7 @@ class MemoryEngine:
676
676
  context: Context about when/why this memory was formed
677
677
  event_date: When the event occurred (defaults to now)
678
678
  document_id: Optional document ID for tracking (always upserts if document already exists)
679
- fact_type_override: Override fact type ('world', 'bank', 'opinion')
679
+ fact_type_override: Override fact type ('world', 'experience', 'opinion')
680
680
  confidence_score: Confidence score for opinions (0.0 to 1.0)
681
681
 
682
682
  Returns:
@@ -728,7 +728,7 @@ class MemoryEngine:
728
728
  - "document_id" (optional): Document ID for this specific content item
729
729
  document_id: **DEPRECATED** - Use "document_id" key in each content dict instead.
730
730
  Applies the same document_id to ALL content items that don't specify their own.
731
- fact_type_override: Override fact type for all facts ('world', 'bank', 'opinion')
731
+ fact_type_override: Override fact type for all facts ('world', 'experience', 'opinion')
732
732
  confidence_score: Confidence score for opinions (0.0 to 1.0)
733
733
 
734
734
  Returns:
@@ -896,7 +896,7 @@ class MemoryEngine:
896
896
  Args:
897
897
  bank_id: bank ID to recall for
898
898
  query: Recall query
899
- fact_type: Required filter for fact type ('world', 'agent', or 'opinion')
899
+ fact_type: Required filter for fact type ('world', 'experience', or 'opinion')
900
900
  budget: Budget level for graph traversal (low=100, mid=300, high=600 units)
901
901
  max_tokens: Maximum tokens to return (counts only 'text' field, default 4096)
902
902
  enable_trace: If True, returns detailed trace object
@@ -936,7 +936,7 @@ class MemoryEngine:
936
936
  Args:
937
937
  bank_id: bank ID to recall for
938
938
  query: Recall query
939
- fact_type: List of fact types to recall (e.g., ['world', 'bank'])
939
+ fact_type: List of fact types to recall (e.g., ['world', 'experience'])
940
940
  budget: Budget level for graph traversal (low=100, mid=300, high=600 units)
941
941
  max_tokens: Maximum tokens to return (counts only 'text' field, default 4096)
942
942
  Results are returned until token budget is reached, stopping before
@@ -1682,13 +1682,15 @@ class MemoryEngine:
1682
1682
 
1683
1683
  Args:
1684
1684
  bank_id: bank ID to delete
1685
- fact_type: Optional fact type filter (world, bank, opinion). If provided, only deletes memories of that type.
1685
+ fact_type: Optional fact type filter (world, experience, opinion). If provided, only deletes memories of that type.
1686
1686
 
1687
1687
  Returns:
1688
1688
  Dictionary with counts of deleted items
1689
1689
  """
1690
1690
  pool = await self._get_pool()
1691
1691
  async with acquire_with_retry(pool) as conn:
1692
+ # Ensure connection is not in read-only mode (can happen with connection poolers)
1693
+ await conn.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE")
1692
1694
  async with conn.transaction():
1693
1695
  try:
1694
1696
  if fact_type:
@@ -1738,7 +1740,7 @@ class MemoryEngine:
1738
1740
 
1739
1741
  Args:
1740
1742
  bank_id: Filter by bank ID
1741
- fact_type: Filter by fact type (world, bank, opinion)
1743
+ fact_type: Filter by fact type (world, experience, opinion)
1742
1744
 
1743
1745
  Returns:
1744
1746
  Dict with nodes, edges, and table_rows
@@ -1912,7 +1914,7 @@ class MemoryEngine:
1912
1914
 
1913
1915
  Args:
1914
1916
  bank_id: Filter by bank ID
1915
- fact_type: Filter by fact type (world, bank, opinion)
1917
+ fact_type: Filter by fact type (world, experience, opinion)
1916
1918
  search_query: Full-text search query (searches text and context fields)
1917
1919
  limit: Maximum number of results to return
1918
1920
  offset: Offset for pagination
@@ -2485,55 +2487,55 @@ Guidelines:
2485
2487
 
2486
2488
  async def get_bank_profile(self, bank_id: str) -> "bank_utils.BankProfile":
2487
2489
  """
2488
- Get bank profile (name, personality + background).
2490
+ Get bank profile (name, disposition + background).
2489
2491
  Auto-creates agent with default values if not exists.
2490
2492
 
2491
2493
  Args:
2492
2494
  bank_id: bank IDentifier
2493
2495
 
2494
2496
  Returns:
2495
- BankProfile with name, typed PersonalityTraits, and background
2497
+ BankProfile with name, typed DispositionTraits, and background
2496
2498
  """
2497
2499
  pool = await self._get_pool()
2498
2500
  return await bank_utils.get_bank_profile(pool, bank_id)
2499
2501
 
2500
- async def update_bank_personality(
2502
+ async def update_bank_disposition(
2501
2503
  self,
2502
2504
  bank_id: str,
2503
- personality: Dict[str, float]
2505
+ disposition: Dict[str, float]
2504
2506
  ) -> None:
2505
2507
  """
2506
- Update bank personality traits.
2508
+ Update bank disposition traits.
2507
2509
 
2508
2510
  Args:
2509
2511
  bank_id: bank IDentifier
2510
- personality: Dict with Big Five traits + bias_strength (all 0-1)
2512
+ disposition: Dict with Big Five traits + bias_strength (all 0-1)
2511
2513
  """
2512
2514
  pool = await self._get_pool()
2513
- await bank_utils.update_bank_personality(pool, bank_id, personality)
2515
+ await bank_utils.update_bank_disposition(pool, bank_id, disposition)
2514
2516
 
2515
2517
  async def merge_bank_background(
2516
2518
  self,
2517
2519
  bank_id: str,
2518
2520
  new_info: str,
2519
- update_personality: bool = True
2521
+ update_disposition: bool = True
2520
2522
  ) -> dict:
2521
2523
  """
2522
2524
  Merge new background information with existing background using LLM.
2523
2525
  Normalizes to first person ("I") and resolves conflicts.
2524
- Optionally infers personality traits from the merged background.
2526
+ Optionally infers disposition traits from the merged background.
2525
2527
 
2526
2528
  Args:
2527
2529
  bank_id: bank IDentifier
2528
2530
  new_info: New background information to add/merge
2529
- update_personality: If True, infer Big Five traits from background (default: True)
2531
+ update_disposition: If True, infer Big Five traits from background (default: True)
2530
2532
 
2531
2533
  Returns:
2532
- Dict with 'background' (str) and optionally 'personality' (dict) keys
2534
+ Dict with 'background' (str) and optionally 'disposition' (dict) keys
2533
2535
  """
2534
2536
  pool = await self._get_pool()
2535
2537
  return await bank_utils.merge_bank_background(
2536
- pool, self._llm_config, bank_id, new_info, update_personality
2538
+ pool, self._llm_config, bank_id, new_info, update_disposition
2537
2539
  )
2538
2540
 
2539
2541
  async def list_banks(self) -> list:
@@ -2541,7 +2543,7 @@ Guidelines:
2541
2543
  List all agents in the system.
2542
2544
 
2543
2545
  Returns:
2544
- List of dicts with bank_id, name, personality, background, created_at, updated_at
2546
+ List of dicts with bank_id, name, disposition, background, created_at, updated_at
2545
2547
  """
2546
2548
  pool = await self._get_pool()
2547
2549
  return await bank_utils.list_banks(pool)
@@ -2559,7 +2561,7 @@ Guidelines:
2559
2561
  Reflect and formulate an answer using bank identity, world facts, and opinions.
2560
2562
 
2561
2563
  This method:
2562
- 1. Retrieves agent facts (bank's identity and past actions)
2564
+ 1. Retrieves experience (conversations and events)
2563
2565
  2. Retrieves world facts (general knowledge)
2564
2566
  3. Retrieves existing opinions (bank's formed perspectives)
2565
2567
  4. Uses LLM to formulate an answer
@@ -2575,7 +2577,7 @@ Guidelines:
2575
2577
  Returns:
2576
2578
  ReflectResult containing:
2577
2579
  - text: Plain text answer (no markdown)
2578
- - based_on: Dict with 'world', 'agent', and 'opinion' fact lists (MemoryFact objects)
2580
+ - based_on: Dict with 'world', 'experience', and 'opinion' fact lists (MemoryFact objects)
2579
2581
  - new_opinions: List of newly formed opinions
2580
2582
  """
2581
2583
  # Use cached LLM config
@@ -2589,7 +2591,7 @@ Guidelines:
2589
2591
  budget=budget,
2590
2592
  max_tokens=4096,
2591
2593
  enable_trace=False,
2592
- fact_type=['agent', 'world', 'opinion'],
2594
+ fact_type=['experience', 'world', 'opinion'],
2593
2595
  include_entities=True
2594
2596
  )
2595
2597
 
@@ -2597,7 +2599,7 @@ Guidelines:
2597
2599
  logger.info(f"[THINK] Search returned {len(all_results)} results")
2598
2600
 
2599
2601
  # Split results by fact type for structured response
2600
- agent_results = [r for r in all_results if r.fact_type == 'bank']
2602
+ agent_results = [r for r in all_results if r.fact_type == 'experience']
2601
2603
  world_results = [r for r in all_results if r.fact_type == 'world']
2602
2604
  opinion_results = [r for r in all_results if r.fact_type == 'opinion']
2603
2605
 
@@ -2610,10 +2612,10 @@ Guidelines:
2610
2612
 
2611
2613
  logger.info(f"[THINK] Formatted facts - agent: {len(agent_facts_text)} chars, world: {len(world_facts_text)} chars, opinion: {len(opinion_facts_text)} chars")
2612
2614
 
2613
- # Get bank profile (name, personality + background)
2615
+ # Get bank profile (name, disposition + background)
2614
2616
  profile = await self.get_bank_profile(bank_id)
2615
2617
  name = profile["name"]
2616
- personality = profile["personality"] # Typed as PersonalityTraits
2618
+ disposition = profile["disposition"] # Typed as DispositionTraits
2617
2619
  background = profile["background"]
2618
2620
 
2619
2621
  # Build the prompt
@@ -2623,14 +2625,14 @@ Guidelines:
2623
2625
  opinion_facts_text=opinion_facts_text,
2624
2626
  query=query,
2625
2627
  name=name,
2626
- personality=personality,
2628
+ disposition=disposition,
2627
2629
  background=background,
2628
2630
  context=context,
2629
2631
  )
2630
2632
 
2631
2633
  logger.info(f"[THINK] Full prompt length: {len(prompt)} chars")
2632
2634
 
2633
- system_message = think_utils.get_system_message(personality)
2635
+ system_message = think_utils.get_system_message(disposition)
2634
2636
 
2635
2637
  answer_text = await self._llm_config.call(
2636
2638
  messages=[
@@ -2657,7 +2659,7 @@ Guidelines:
2657
2659
  text=answer_text,
2658
2660
  based_on={
2659
2661
  "world": world_results,
2660
- "agent": agent_results,
2662
+ "experience": agent_results,
2661
2663
  "opinion": opinion_results
2662
2664
  },
2663
2665
  new_opinions=[] # Opinions are being extracted asynchronously
@@ -2871,7 +2873,7 @@ Guidelines:
2871
2873
  JOIN unit_entities ue ON mu.id = ue.unit_id
2872
2874
  WHERE mu.bank_id = $1
2873
2875
  AND ue.entity_id = $2
2874
- AND mu.fact_type IN ('world', 'agent')
2876
+ AND mu.fact_type IN ('world', 'experience')
2875
2877
  ORDER BY mu.occurred_start DESC
2876
2878
  LIMIT 50
2877
2879
  """,
@@ -10,9 +10,9 @@ from typing import Optional, List, Dict, Any
10
10
  from pydantic import BaseModel, Field, ConfigDict
11
11
 
12
12
 
13
- class PersonalityTraits(BaseModel):
13
+ class DispositionTraits(BaseModel):
14
14
  """
15
- Personality traits for a bank using the Big Five model.
15
+ Disposition traits for a bank using the Big Five model.
16
16
 
17
17
  All traits are scored 0.0-1.0 where higher values indicate stronger presence of the trait.
18
18
  """
@@ -21,7 +21,7 @@ class PersonalityTraits(BaseModel):
21
21
  extraversion: float = Field(description="Extraversion and sociability (0.0-1.0)")
22
22
  agreeableness: float = Field(description="Agreeableness and cooperation (0.0-1.0)")
23
23
  neuroticism: float = Field(description="Emotional sensitivity and neuroticism (0.0-1.0)")
24
- bias_strength: float = Field(description="How strongly personality influences thinking (0.0-1.0)")
24
+ bias_strength: float = Field(description="How strongly disposition influences thinking (0.0-1.0)")
25
25
 
26
26
  model_config = ConfigDict(json_schema_extra={
27
27
  "example": {
@@ -61,7 +61,7 @@ class MemoryFact(BaseModel):
61
61
 
62
62
  id: str = Field(description="Unique identifier for the memory fact")
63
63
  text: str = Field(description="The actual text content of the memory")
64
- fact_type: str = Field(description="Type of fact: 'world', 'bank', 'opinion', or 'observation'")
64
+ fact_type: str = Field(description="Type of fact: 'world', 'experience', 'opinion', or 'observation'")
65
65
  entities: Optional[List[str]] = Field(None, description="Entity names mentioned in this fact")
66
66
  context: Optional[str] = Field(None, description="Additional context for the memory")
67
67
  occurred_start: Optional[str] = Field(None, description="ISO format date when the event started occurring")
@@ -142,7 +142,7 @@ class ReflectResult(BaseModel):
142
142
  "occurred_end": "2024-01-15T10:30:00Z"
143
143
  }
144
144
  ],
145
- "agent": [],
145
+ "experience": [],
146
146
  "opinion": []
147
147
  },
148
148
  "new_opinions": [
@@ -153,7 +153,7 @@ class ReflectResult(BaseModel):
153
153
 
154
154
  text: str = Field(description="The formulated answer text")
155
155
  based_on: Dict[str, List[MemoryFact]] = Field(
156
- description="Facts used to formulate the answer, organized by type (world, agent, opinion)"
156
+ description="Facts used to formulate the answer, organized by type (world, experience, opinion)"
157
157
  )
158
158
  new_opinions: List[str] = Field(
159
159
  default_factory=list,
@@ -1,5 +1,5 @@
1
1
  """
2
- bank profile utilities for personality and background management.
2
+ bank profile utilities for disposition and background management.
3
3
  """
4
4
 
5
5
  import json
@@ -8,11 +8,11 @@ import re
8
8
  from typing import Dict, Optional, TypedDict
9
9
  from pydantic import BaseModel, Field
10
10
  from ..db_utils import acquire_with_retry
11
- from ..response_models import PersonalityTraits
11
+ from ..response_models import DispositionTraits
12
12
 
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
- DEFAULT_PERSONALITY = {
15
+ DEFAULT_DISPOSITION = {
16
16
  "openness": 0.5,
17
17
  "conscientiousness": 0.5,
18
18
  "extraversion": 0.5,
@@ -25,19 +25,19 @@ DEFAULT_PERSONALITY = {
25
25
  class BankProfile(TypedDict):
26
26
  """Type for bank profile data."""
27
27
  name: str
28
- personality: PersonalityTraits
28
+ disposition: DispositionTraits
29
29
  background: str
30
30
 
31
31
 
32
32
  class BackgroundMergeResponse(BaseModel):
33
- """LLM response for background merge with personality inference."""
33
+ """LLM response for background merge with disposition inference."""
34
34
  background: str = Field(description="Merged background in first person perspective")
35
- personality: PersonalityTraits = Field(description="Inferred Big Five personality traits")
35
+ disposition: DispositionTraits = Field(description="Inferred Big Five disposition traits")
36
36
 
37
37
 
38
38
  async def get_bank_profile(pool, bank_id: str) -> BankProfile:
39
39
  """
40
- Get bank profile (name, personality + background).
40
+ Get bank profile (name, disposition + background).
41
41
  Auto-creates bank with default values if not exists.
42
42
 
43
43
  Args:
@@ -45,13 +45,13 @@ async def get_bank_profile(pool, bank_id: str) -> BankProfile:
45
45
  bank_id: bank IDentifier
46
46
 
47
47
  Returns:
48
- BankProfile with name, typed PersonalityTraits, and background
48
+ BankProfile with name, typed DispositionTraits, and background
49
49
  """
50
50
  async with acquire_with_retry(pool) as conn:
51
51
  # Try to get existing bank
52
52
  row = await conn.fetchrow(
53
53
  """
54
- SELECT name, personality, background
54
+ SELECT name, disposition, background
55
55
  FROM banks WHERE bank_id = $1
56
56
  """,
57
57
  bank_id
@@ -59,48 +59,48 @@ async def get_bank_profile(pool, bank_id: str) -> BankProfile:
59
59
 
60
60
  if row:
61
61
  # asyncpg returns JSONB as a string, so parse it
62
- personality_data = row["personality"]
63
- if isinstance(personality_data, str):
64
- personality_data = json.loads(personality_data)
62
+ disposition_data = row["disposition"]
63
+ if isinstance(disposition_data, str):
64
+ disposition_data = json.loads(disposition_data)
65
65
 
66
66
  return BankProfile(
67
67
  name=row["name"],
68
- personality=PersonalityTraits(**personality_data),
68
+ disposition=DispositionTraits(**disposition_data),
69
69
  background=row["background"]
70
70
  )
71
71
 
72
72
  # Bank doesn't exist, create with defaults
73
73
  await conn.execute(
74
74
  """
75
- INSERT INTO banks (bank_id, name, personality, background)
75
+ INSERT INTO banks (bank_id, name, disposition, background)
76
76
  VALUES ($1, $2, $3::jsonb, $4)
77
77
  ON CONFLICT (bank_id) DO NOTHING
78
78
  """,
79
79
  bank_id,
80
80
  bank_id, # Default name is the bank_id
81
- json.dumps(DEFAULT_PERSONALITY),
81
+ json.dumps(DEFAULT_DISPOSITION),
82
82
  ""
83
83
  )
84
84
 
85
85
  return BankProfile(
86
86
  name=bank_id,
87
- personality=PersonalityTraits(**DEFAULT_PERSONALITY),
87
+ disposition=DispositionTraits(**DEFAULT_DISPOSITION),
88
88
  background=""
89
89
  )
90
90
 
91
91
 
92
- async def update_bank_personality(
92
+ async def update_bank_disposition(
93
93
  pool,
94
94
  bank_id: str,
95
- personality: Dict[str, float]
95
+ disposition: Dict[str, float]
96
96
  ) -> None:
97
97
  """
98
- Update bank personality traits.
98
+ Update bank disposition traits.
99
99
 
100
100
  Args:
101
101
  pool: Database connection pool
102
102
  bank_id: bank IDentifier
103
- personality: Dict with Big Five traits + bias_strength (all 0-1)
103
+ disposition: Dict with Big Five traits + bias_strength (all 0-1)
104
104
  """
105
105
  # Ensure bank exists first
106
106
  await get_bank_profile(pool, bank_id)
@@ -109,12 +109,12 @@ async def update_bank_personality(
109
109
  await conn.execute(
110
110
  """
111
111
  UPDATE banks
112
- SET personality = $2::jsonb,
112
+ SET disposition = $2::jsonb,
113
113
  updated_at = NOW()
114
114
  WHERE bank_id = $1
115
115
  """,
116
116
  bank_id,
117
- json.dumps(personality)
117
+ json.dumps(disposition)
118
118
  )
119
119
 
120
120
 
@@ -123,53 +123,53 @@ async def merge_bank_background(
123
123
  llm_config,
124
124
  bank_id: str,
125
125
  new_info: str,
126
- update_personality: bool = True
126
+ update_disposition: bool = True
127
127
  ) -> dict:
128
128
  """
129
129
  Merge new background information with existing background using LLM.
130
130
  Normalizes to first person ("I") and resolves conflicts.
131
- Optionally infers personality traits from the merged background.
131
+ Optionally infers disposition traits from the merged background.
132
132
 
133
133
  Args:
134
134
  pool: Database connection pool
135
135
  llm_config: LLM configuration for background merging
136
136
  bank_id: bank IDentifier
137
137
  new_info: New background information to add/merge
138
- update_personality: If True, infer Big Five traits from background (default: True)
138
+ update_disposition: If True, infer Big Five traits from background (default: True)
139
139
 
140
140
  Returns:
141
- Dict with 'background' (str) and optionally 'personality' (dict) keys
141
+ Dict with 'background' (str) and optionally 'disposition' (dict) keys
142
142
  """
143
143
  # Get current profile
144
144
  profile = await get_bank_profile(pool, bank_id)
145
145
  current_background = profile["background"]
146
146
 
147
- # Use LLM to merge backgrounds and optionally infer personality
147
+ # Use LLM to merge backgrounds and optionally infer disposition
148
148
  result = await _llm_merge_background(
149
149
  llm_config,
150
150
  current_background,
151
151
  new_info,
152
- infer_personality=update_personality
152
+ infer_disposition=update_disposition
153
153
  )
154
154
 
155
155
  merged_background = result["background"]
156
- inferred_personality = result.get("personality")
156
+ inferred_disposition = result.get("disposition")
157
157
 
158
158
  # Update in database
159
159
  async with acquire_with_retry(pool) as conn:
160
- if inferred_personality:
161
- # Update both background and personality
160
+ if inferred_disposition:
161
+ # Update both background and disposition
162
162
  await conn.execute(
163
163
  """
164
164
  UPDATE banks
165
165
  SET background = $2,
166
- personality = $3::jsonb,
166
+ disposition = $3::jsonb,
167
167
  updated_at = NOW()
168
168
  WHERE bank_id = $1
169
169
  """,
170
170
  bank_id,
171
171
  merged_background,
172
- json.dumps(inferred_personality)
172
+ json.dumps(inferred_disposition)
173
173
  )
174
174
  else:
175
175
  # Update only background
@@ -185,8 +185,8 @@ async def merge_bank_background(
185
185
  )
186
186
 
187
187
  response = {"background": merged_background}
188
- if inferred_personality:
189
- response["personality"] = inferred_personality
188
+ if inferred_disposition:
189
+ response["disposition"] = inferred_disposition
190
190
 
191
191
  return response
192
192
 
@@ -195,23 +195,23 @@ async def _llm_merge_background(
195
195
  llm_config,
196
196
  current: str,
197
197
  new_info: str,
198
- infer_personality: bool = False
198
+ infer_disposition: bool = False
199
199
  ) -> dict:
200
200
  """
201
201
  Use LLM to intelligently merge background information.
202
- Optionally infer Big Five personality traits from the merged background.
202
+ Optionally infer Big Five disposition traits from the merged background.
203
203
 
204
204
  Args:
205
205
  llm_config: LLM configuration to use
206
206
  current: Current background text
207
207
  new_info: New information to merge
208
- infer_personality: If True, also infer personality traits
208
+ infer_disposition: If True, also infer disposition traits
209
209
 
210
210
  Returns:
211
- Dict with 'background' (str) and optionally 'personality' (dict) keys
211
+ Dict with 'background' (str) and optionally 'disposition' (dict) keys
212
212
  """
213
- if infer_personality:
214
- prompt = f"""You are helping maintain a memory bank's background/profile and infer their personality. You MUST respond with ONLY valid JSON.
213
+ if infer_disposition:
214
+ prompt = f"""You are helping maintain a memory bank's background/profile and infer their disposition. You MUST respond with ONLY valid JSON.
215
215
 
216
216
  Current background: {current if current else "(empty)"}
217
217
 
@@ -223,20 +223,20 @@ Instructions:
223
223
  3. Keep additions that don't conflict
224
224
  4. Output in FIRST PERSON ("I") perspective
225
225
  5. Be concise - keep merged background under 500 characters
226
- 6. Infer Big Five personality traits from the merged background:
226
+ 6. Infer Big Five disposition traits from the merged background:
227
227
  - Openness: 0.0-1.0 (creativity, curiosity, openness to new ideas)
228
228
  - Conscientiousness: 0.0-1.0 (organization, discipline, goal-directed)
229
229
  - Extraversion: 0.0-1.0 (sociability, assertiveness, energy from others)
230
230
  - Agreeableness: 0.0-1.0 (cooperation, empathy, consideration)
231
231
  - Neuroticism: 0.0-1.0 (emotional sensitivity, anxiety, stress response)
232
- - Bias Strength: 0.0-1.0 (how much personality influences opinions)
232
+ - Bias Strength: 0.0-1.0 (how much disposition influences opinions)
233
233
 
234
234
  CRITICAL: You MUST respond with ONLY a valid JSON object. No markdown, no code blocks, no explanations. Just the JSON.
235
235
 
236
236
  Format:
237
237
  {{
238
238
  "background": "the merged background text in first person",
239
- "personality": {{
239
+ "disposition": {{
240
240
  "openness": 0.7,
241
241
  "conscientiousness": 0.6,
242
242
  "extraversion": 0.5,
@@ -274,8 +274,8 @@ Merged background:"""
274
274
  # Prepare messages
275
275
  messages = [{"role": "user", "content": prompt}]
276
276
 
277
- if infer_personality:
278
- # Use structured output with Pydantic model for personality inference
277
+ if infer_disposition:
278
+ # Use structured output with Pydantic model for disposition inference
279
279
  try:
280
280
  parsed = await llm_config.call(
281
281
  messages=messages,
@@ -289,13 +289,13 @@ Merged background:"""
289
289
  # Convert Pydantic model to dict format
290
290
  return {
291
291
  "background": parsed.background,
292
- "personality": parsed.personality.model_dump()
292
+ "disposition": parsed.disposition.model_dump()
293
293
  }
294
294
  except Exception as e:
295
295
  logger.warning(f"Structured output failed, falling back to manual parsing: {e}")
296
296
  # Fall through to manual parsing below
297
297
 
298
- # Manual parsing fallback or non-personality merge
298
+ # Manual parsing fallback or non-disposition merge
299
299
  content = await llm_config.call(
300
300
  messages=messages,
301
301
  scope="bank_background",
@@ -305,7 +305,7 @@ Merged background:"""
305
305
 
306
306
  logger.info(f"LLM response for background merge (first 500 chars): {content[:500]}")
307
307
 
308
- if infer_personality:
308
+ if infer_disposition:
309
309
  # Parse JSON response - try multiple extraction methods
310
310
  result = None
311
311
 
@@ -330,7 +330,7 @@ Merged background:"""
330
330
  # Method 3: Find nested JSON structure
331
331
  if result is None:
332
332
  # Look for JSON object with nested structure
333
- json_match = re.search(r'\{[^{}]*"background"[^{}]*"personality"[^{}]*\{[^{}]*\}[^{}]*\}', content, re.DOTALL)
333
+ json_match = re.search(r'\{[^{}]*"background"[^{}]*"disposition"[^{}]*\{[^{}]*\}[^{}]*\}', content, re.DOTALL)
334
334
  if json_match:
335
335
  try:
336
336
  result = json.loads(json_match.group())
@@ -341,23 +341,23 @@ Merged background:"""
341
341
  # All parsing methods failed - use fallback
342
342
  if result is None:
343
343
  logger.warning(f"Failed to extract JSON from LLM response. Raw content: {content[:200]}")
344
- # Fallback: use new_info as background with default personality
344
+ # Fallback: use new_info as background with default disposition
345
345
  return {
346
346
  "background": new_info if new_info else current if current else "",
347
- "personality": DEFAULT_PERSONALITY.copy()
347
+ "disposition": DEFAULT_DISPOSITION.copy()
348
348
  }
349
349
 
350
- # Validate personality values
351
- personality = result.get("personality", {})
350
+ # Validate disposition values
351
+ disposition = result.get("disposition", {})
352
352
  for key in ["openness", "conscientiousness", "extraversion",
353
353
  "agreeableness", "neuroticism", "bias_strength"]:
354
- if key not in personality:
355
- personality[key] = 0.5 # Default to neutral
354
+ if key not in disposition:
355
+ disposition[key] = 0.5 # Default to neutral
356
356
  else:
357
357
  # Clamp to [0, 1]
358
- personality[key] = max(0.0, min(1.0, float(personality[key])))
358
+ disposition[key] = max(0.0, min(1.0, float(disposition[key])))
359
359
 
360
- result["personality"] = personality
360
+ result["disposition"] = disposition
361
361
 
362
362
  # Ensure background exists
363
363
  if "background" not in result or not result["background"]:
@@ -380,8 +380,8 @@ Merged background:"""
380
380
  merged = new_info
381
381
 
382
382
  result = {"background": merged}
383
- if infer_personality:
384
- result["personality"] = DEFAULT_PERSONALITY.copy()
383
+ if infer_disposition:
384
+ result["disposition"] = DEFAULT_DISPOSITION.copy()
385
385
  return result
386
386
 
387
387
 
@@ -393,12 +393,12 @@ async def list_banks(pool) -> list:
393
393
  pool: Database connection pool
394
394
 
395
395
  Returns:
396
- List of dicts with bank_id, name, personality, background, created_at, updated_at
396
+ List of dicts with bank_id, name, disposition, background, created_at, updated_at
397
397
  """
398
398
  async with acquire_with_retry(pool) as conn:
399
399
  rows = await conn.fetch(
400
400
  """
401
- SELECT bank_id, name, personality, background, created_at, updated_at
401
+ SELECT bank_id, name, disposition, background, created_at, updated_at
402
402
  FROM banks
403
403
  ORDER BY updated_at DESC
404
404
  """
@@ -407,14 +407,14 @@ async def list_banks(pool) -> list:
407
407
  result = []
408
408
  for row in rows:
409
409
  # asyncpg returns JSONB as a string, so parse it
410
- personality_data = row["personality"]
411
- if isinstance(personality_data, str):
412
- personality_data = json.loads(personality_data)
410
+ disposition_data = row["disposition"]
411
+ if isinstance(disposition_data, str):
412
+ disposition_data = json.loads(disposition_data)
413
413
 
414
414
  result.append({
415
415
  "bank_id": row["bank_id"],
416
416
  "name": row["name"],
417
- "personality": personality_data,
417
+ "disposition": disposition_data,
418
418
  "background": row["background"],
419
419
  "created_at": row["created_at"].isoformat() if row["created_at"] else None,
420
420
  "updated_at": row["updated_at"].isoformat() if row["updated_at"] else None,