hindsight-api 0.0.18__tar.gz → 0.0.21__tar.gz
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.
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/.gitignore +3 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/PKG-INFO +2 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/alembic/env.py +17 -0
- hindsight_api-0.0.21/alembic/versions/d9f6a3b4c5e2_rename_bank_to_interactions.py +48 -0
- hindsight_api-0.0.21/alembic/versions/rename_personality_to_disposition.py +65 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/api/__init__.py +2 -2
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/api/http.py +60 -60
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/api/mcp.py +1 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/llm_wrapper.py +140 -5
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/memory_engine.py +33 -31
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/response_models.py +6 -6
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/bank_utils.py +66 -66
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/fact_extraction.py +8 -8
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/fact_storage.py +1 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/link_utils.py +112 -43
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/types.py +1 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/think_utils.py +20 -20
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/trace.py +1 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/models.py +3 -3
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/pyproject.toml +2 -1
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_agents_api.py +2 -2
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/README.md +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/alembic/README +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/alembic/script.py.mako +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/alembic/versions/5a366d414dce_initial_schema.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/alembic/versions/b7c4d8e9f1a2_add_chunks_table.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/alembic/versions/c8e5f2a3b4d1_add_retain_params_to_documents.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/__init__.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/cli.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/__init__.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/cross_encoder.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/db_utils.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/embeddings.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/entity_resolver.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/query_analyzer.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/__init__.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/chunk_storage.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/deduplication.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/embedding_processing.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/embedding_utils.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/entity_processing.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/link_creation.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/retain/orchestrator.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/__init__.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/fusion.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/observation_utils.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/reranking.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/retrieval.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/scoring.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/temporal_extraction.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/tracer.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/search/types.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/task_backend.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/engine/utils.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/metrics.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/migrations.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/pg0.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/web/__init__.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/hindsight_api/web/server.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/test_chunks_debug.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/test_mentioned_at.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/RETAIN_TEST_COVERAGE_PLAN.md +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/__init__.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/conftest.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/fixtures/README.md +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/fixtures/locomo_conversation_sample.json +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_batch_chunking.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_chunking.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_document_tracking.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_fact_extraction_quality.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_fact_ordering.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_http_api_integration.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_link_utils.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_mcp_api_integration.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_mcp_routing.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_observations.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_query_analyzer.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_retain.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_search_trace.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_temporal_ranges.py +0 -0
- {hindsight_api-0.0.18 → hindsight_api-0.0.21}/tests/test_think.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hindsight-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.21
|
|
4
4
|
Summary: Temporal + Semantic + Entity Memory System for AI agents using PostgreSQL
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: alembic>=1.17.1
|
|
@@ -8,6 +8,7 @@ Requires-Dist: asyncpg>=0.29.0
|
|
|
8
8
|
Requires-Dist: dateparser>=1.2.2
|
|
9
9
|
Requires-Dist: fastapi[standard]>=0.120.3
|
|
10
10
|
Requires-Dist: fastmcp>=2.0.0
|
|
11
|
+
Requires-Dist: google-genai>=1.0.0
|
|
11
12
|
Requires-Dist: greenlet>=3.2.4
|
|
12
13
|
Requires-Dist: httpx>=0.27.0
|
|
13
14
|
Requires-Dist: langchain-text-splitters>=0.3.0
|
|
@@ -105,6 +105,8 @@ def run_migrations_offline() -> None:
|
|
|
105
105
|
|
|
106
106
|
def run_migrations_online() -> None:
|
|
107
107
|
"""Run migrations in 'online' mode with synchronous engine."""
|
|
108
|
+
from sqlalchemy import event, text
|
|
109
|
+
|
|
108
110
|
get_database_url() # Process and set the database URL in config
|
|
109
111
|
|
|
110
112
|
connectable = engine_from_config(
|
|
@@ -113,7 +115,19 @@ def run_migrations_online() -> None:
|
|
|
113
115
|
poolclass=pool.NullPool,
|
|
114
116
|
)
|
|
115
117
|
|
|
118
|
+
# Add event listener to ensure connection is in read-write mode
|
|
119
|
+
# This is needed for Supabase which may start connections in read-only mode
|
|
120
|
+
@event.listens_for(connectable, "connect")
|
|
121
|
+
def set_read_write_mode(dbapi_connection, connection_record):
|
|
122
|
+
cursor = dbapi_connection.cursor()
|
|
123
|
+
cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE")
|
|
124
|
+
cursor.close()
|
|
125
|
+
|
|
116
126
|
with connectable.connect() as connection:
|
|
127
|
+
# Also explicitly set read-write mode on this connection
|
|
128
|
+
connection.execute(text("SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE"))
|
|
129
|
+
connection.commit() # Commit the SET command
|
|
130
|
+
|
|
117
131
|
context.configure(
|
|
118
132
|
connection=connection,
|
|
119
133
|
target_metadata=target_metadata
|
|
@@ -122,6 +136,9 @@ def run_migrations_online() -> None:
|
|
|
122
136
|
with context.begin_transaction():
|
|
123
137
|
context.run_migrations()
|
|
124
138
|
|
|
139
|
+
# Explicit commit to ensure changes are persisted (especially for Supabase)
|
|
140
|
+
connection.commit()
|
|
141
|
+
|
|
125
142
|
|
|
126
143
|
if context.is_offline_mode():
|
|
127
144
|
run_migrations_offline()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Rename fact_type 'bank' to 'experience'
|
|
2
|
+
|
|
3
|
+
Revision ID: d9f6a3b4c5e2
|
|
4
|
+
Revises: c8e5f2a3b4d1
|
|
5
|
+
Create Date: 2024-12-04 15:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'd9f6a3b4c5e2'
|
|
14
|
+
down_revision = 'c8e5f2a3b4d1'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# Drop old check constraint FIRST (before updating data)
|
|
21
|
+
op.drop_constraint('memory_units_fact_type_check', 'memory_units', type_='check')
|
|
22
|
+
|
|
23
|
+
# Update existing 'bank' values to 'experience'
|
|
24
|
+
op.execute("UPDATE memory_units SET fact_type = 'experience' WHERE fact_type = 'bank'")
|
|
25
|
+
# Also update any 'interactions' values (in case of partial migration)
|
|
26
|
+
op.execute("UPDATE memory_units SET fact_type = 'experience' WHERE fact_type = 'interactions'")
|
|
27
|
+
|
|
28
|
+
# Create new check constraint with 'experience' instead of 'bank'
|
|
29
|
+
op.create_check_constraint(
|
|
30
|
+
'memory_units_fact_type_check',
|
|
31
|
+
'memory_units',
|
|
32
|
+
"fact_type IN ('world', 'experience', 'opinion', 'observation')"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def downgrade():
|
|
37
|
+
# Drop new check constraint FIRST
|
|
38
|
+
op.drop_constraint('memory_units_fact_type_check', 'memory_units', type_='check')
|
|
39
|
+
|
|
40
|
+
# Update 'experience' back to 'bank'
|
|
41
|
+
op.execute("UPDATE memory_units SET fact_type = 'bank' WHERE fact_type = 'experience'")
|
|
42
|
+
|
|
43
|
+
# Recreate old check constraint
|
|
44
|
+
op.create_check_constraint(
|
|
45
|
+
'memory_units_fact_type_check',
|
|
46
|
+
'memory_units',
|
|
47
|
+
"fact_type IN ('world', 'bank', 'opinion', 'observation')"
|
|
48
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""rename_personality_to_disposition
|
|
2
|
+
|
|
3
|
+
Revision ID: rename_personality
|
|
4
|
+
Revises: d9f6a3b4c5e2
|
|
5
|
+
Create Date: 2024-12-04
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
from sqlalchemy.dialects import postgresql
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = 'rename_personality'
|
|
17
|
+
down_revision: Union[str, Sequence[str], None] = 'd9f6a3b4c5e2'
|
|
18
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
19
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
"""Rename personality column to disposition in banks table (if it exists)."""
|
|
24
|
+
conn = op.get_bind()
|
|
25
|
+
|
|
26
|
+
# Check if 'personality' column exists (old database)
|
|
27
|
+
result = conn.execute(sa.text("""
|
|
28
|
+
SELECT column_name
|
|
29
|
+
FROM information_schema.columns
|
|
30
|
+
WHERE table_name = 'banks' AND column_name = 'personality'
|
|
31
|
+
"""))
|
|
32
|
+
has_personality = result.fetchone() is not None
|
|
33
|
+
|
|
34
|
+
# Check if 'disposition' column exists (new database)
|
|
35
|
+
result = conn.execute(sa.text("""
|
|
36
|
+
SELECT column_name
|
|
37
|
+
FROM information_schema.columns
|
|
38
|
+
WHERE table_name = 'banks' AND column_name = 'disposition'
|
|
39
|
+
"""))
|
|
40
|
+
has_disposition = result.fetchone() is not None
|
|
41
|
+
|
|
42
|
+
if has_personality and not has_disposition:
|
|
43
|
+
# Old database: rename personality -> disposition
|
|
44
|
+
op.alter_column('banks', 'personality', new_column_name='disposition')
|
|
45
|
+
elif not has_personality and not has_disposition:
|
|
46
|
+
# Neither exists (shouldn't happen, but be safe): add disposition column
|
|
47
|
+
op.add_column('banks', sa.Column(
|
|
48
|
+
'disposition',
|
|
49
|
+
postgresql.JSONB(astext_type=sa.Text()),
|
|
50
|
+
server_default=sa.text("'{}'::jsonb"),
|
|
51
|
+
nullable=False
|
|
52
|
+
))
|
|
53
|
+
# else: disposition already exists, nothing to do
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def downgrade() -> None:
|
|
57
|
+
"""Revert disposition column back to personality."""
|
|
58
|
+
conn = op.get_bind()
|
|
59
|
+
result = conn.execute(sa.text("""
|
|
60
|
+
SELECT column_name
|
|
61
|
+
FROM information_schema.columns
|
|
62
|
+
WHERE table_name = 'banks' AND column_name = 'disposition'
|
|
63
|
+
"""))
|
|
64
|
+
if result.fetchone():
|
|
65
|
+
op.alter_column('banks', 'disposition', new_column_name='personality')
|
|
@@ -87,7 +87,7 @@ from .http import (
|
|
|
87
87
|
ReflectRequest,
|
|
88
88
|
ReflectResponse,
|
|
89
89
|
CreateBankRequest,
|
|
90
|
-
|
|
90
|
+
DispositionTraits,
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
__all__ = [
|
|
@@ -100,5 +100,5 @@ __all__ = [
|
|
|
100
100
|
"ReflectRequest",
|
|
101
101
|
"ReflectResponse",
|
|
102
102
|
"CreateBankRequest",
|
|
103
|
-
"
|
|
103
|
+
"DispositionTraits",
|
|
104
104
|
]
|
|
@@ -84,7 +84,7 @@ class RecallRequest(BaseModel):
|
|
|
84
84
|
model_config = ConfigDict(json_schema_extra={
|
|
85
85
|
"example": {
|
|
86
86
|
"query": "What did Alice say about machine learning?",
|
|
87
|
-
"types": ["world", "
|
|
87
|
+
"types": ["world", "experience"],
|
|
88
88
|
"budget": "mid",
|
|
89
89
|
"max_tokens": 4096,
|
|
90
90
|
"trace": True,
|
|
@@ -131,7 +131,7 @@ class RecallResult(BaseModel):
|
|
|
131
131
|
|
|
132
132
|
id: str
|
|
133
133
|
text: str
|
|
134
|
-
type: Optional[str] = None # fact type: world,
|
|
134
|
+
type: Optional[str] = None # fact type: world, experience, opinion, observation
|
|
135
135
|
entities: Optional[List[str]] = None # Entity names mentioned in this fact
|
|
136
136
|
context: Optional[str] = None
|
|
137
137
|
occurred_start: Optional[str] = None # ISO format date when the event started
|
|
@@ -397,7 +397,7 @@ class ReflectFact(BaseModel):
|
|
|
397
397
|
|
|
398
398
|
id: Optional[str] = None
|
|
399
399
|
text: str
|
|
400
|
-
type: Optional[str] = None # fact type: world,
|
|
400
|
+
type: Optional[str] = None # fact type: world, experience, opinion
|
|
401
401
|
context: Optional[str] = None
|
|
402
402
|
occurred_start: Optional[str] = None
|
|
403
403
|
occurred_end: Optional[str] = None
|
|
@@ -417,7 +417,7 @@ class ReflectResponse(BaseModel):
|
|
|
417
417
|
{
|
|
418
418
|
"id": "456",
|
|
419
419
|
"text": "I discussed AI applications last week",
|
|
420
|
-
"type": "
|
|
420
|
+
"type": "experience"
|
|
421
421
|
}
|
|
422
422
|
]
|
|
423
423
|
}
|
|
@@ -438,8 +438,8 @@ class BanksResponse(BaseModel):
|
|
|
438
438
|
banks: List[str]
|
|
439
439
|
|
|
440
440
|
|
|
441
|
-
class
|
|
442
|
-
"""
|
|
441
|
+
class DispositionTraits(BaseModel):
|
|
442
|
+
"""Disposition traits based on Big Five model."""
|
|
443
443
|
model_config = ConfigDict(json_schema_extra={
|
|
444
444
|
"example": {
|
|
445
445
|
"openness": 0.8,
|
|
@@ -456,7 +456,7 @@ class PersonalityTraits(BaseModel):
|
|
|
456
456
|
extraversion: float = Field(ge=0.0, le=1.0, description="Extraversion (0-1)")
|
|
457
457
|
agreeableness: float = Field(ge=0.0, le=1.0, description="Agreeableness (0-1)")
|
|
458
458
|
neuroticism: float = Field(ge=0.0, le=1.0, description="Neuroticism (0-1)")
|
|
459
|
-
bias_strength: float = Field(ge=0.0, le=1.0, description="How strongly
|
|
459
|
+
bias_strength: float = Field(ge=0.0, le=1.0, description="How strongly disposition influences opinions (0-1)")
|
|
460
460
|
|
|
461
461
|
|
|
462
462
|
class BankProfileResponse(BaseModel):
|
|
@@ -465,7 +465,7 @@ class BankProfileResponse(BaseModel):
|
|
|
465
465
|
"example": {
|
|
466
466
|
"bank_id": "user123",
|
|
467
467
|
"name": "Alice",
|
|
468
|
-
"
|
|
468
|
+
"disposition": {
|
|
469
469
|
"openness": 0.8,
|
|
470
470
|
"conscientiousness": 0.6,
|
|
471
471
|
"extraversion": 0.5,
|
|
@@ -479,13 +479,13 @@ class BankProfileResponse(BaseModel):
|
|
|
479
479
|
|
|
480
480
|
bank_id: str
|
|
481
481
|
name: str
|
|
482
|
-
|
|
482
|
+
disposition: DispositionTraits
|
|
483
483
|
background: str
|
|
484
484
|
|
|
485
485
|
|
|
486
|
-
class
|
|
487
|
-
"""Request model for updating
|
|
488
|
-
|
|
486
|
+
class UpdateDispositionRequest(BaseModel):
|
|
487
|
+
"""Request model for updating disposition traits."""
|
|
488
|
+
disposition: DispositionTraits
|
|
489
489
|
|
|
490
490
|
|
|
491
491
|
class AddBackgroundRequest(BaseModel):
|
|
@@ -493,14 +493,14 @@ class AddBackgroundRequest(BaseModel):
|
|
|
493
493
|
model_config = ConfigDict(json_schema_extra={
|
|
494
494
|
"example": {
|
|
495
495
|
"content": "I was born in Texas",
|
|
496
|
-
"
|
|
496
|
+
"update_disposition": True
|
|
497
497
|
}
|
|
498
498
|
})
|
|
499
499
|
|
|
500
500
|
content: str = Field(description="New background information to add or merge")
|
|
501
|
-
|
|
501
|
+
update_disposition: bool = Field(
|
|
502
502
|
default=True,
|
|
503
|
-
description="If true, infer Big Five
|
|
503
|
+
description="If true, infer Big Five disposition traits from the merged background (default: true)"
|
|
504
504
|
)
|
|
505
505
|
|
|
506
506
|
|
|
@@ -509,7 +509,7 @@ class BackgroundResponse(BaseModel):
|
|
|
509
509
|
model_config = ConfigDict(json_schema_extra={
|
|
510
510
|
"example": {
|
|
511
511
|
"background": "I was born in Texas. I am a software engineer with 10 years of experience.",
|
|
512
|
-
"
|
|
512
|
+
"disposition": {
|
|
513
513
|
"openness": 0.7,
|
|
514
514
|
"conscientiousness": 0.6,
|
|
515
515
|
"extraversion": 0.5,
|
|
@@ -521,14 +521,14 @@ class BackgroundResponse(BaseModel):
|
|
|
521
521
|
})
|
|
522
522
|
|
|
523
523
|
background: str
|
|
524
|
-
|
|
524
|
+
disposition: Optional[DispositionTraits] = None
|
|
525
525
|
|
|
526
526
|
|
|
527
527
|
class BankListItem(BaseModel):
|
|
528
528
|
"""Bank list item with profile summary."""
|
|
529
529
|
bank_id: str
|
|
530
530
|
name: str
|
|
531
|
-
|
|
531
|
+
disposition: DispositionTraits
|
|
532
532
|
background: str
|
|
533
533
|
created_at: Optional[str] = None
|
|
534
534
|
updated_at: Optional[str] = None
|
|
@@ -542,7 +542,7 @@ class BankListResponse(BaseModel):
|
|
|
542
542
|
{
|
|
543
543
|
"bank_id": "user123",
|
|
544
544
|
"name": "Alice",
|
|
545
|
-
"
|
|
545
|
+
"disposition": {
|
|
546
546
|
"openness": 0.5,
|
|
547
547
|
"conscientiousness": 0.5,
|
|
548
548
|
"extraversion": 0.5,
|
|
@@ -566,7 +566,7 @@ class CreateBankRequest(BaseModel):
|
|
|
566
566
|
model_config = ConfigDict(json_schema_extra={
|
|
567
567
|
"example": {
|
|
568
568
|
"name": "Alice",
|
|
569
|
-
"
|
|
569
|
+
"disposition": {
|
|
570
570
|
"openness": 0.8,
|
|
571
571
|
"conscientiousness": 0.6,
|
|
572
572
|
"extraversion": 0.5,
|
|
@@ -579,7 +579,7 @@ class CreateBankRequest(BaseModel):
|
|
|
579
579
|
})
|
|
580
580
|
|
|
581
581
|
name: Optional[str] = None
|
|
582
|
-
|
|
582
|
+
disposition: Optional[DispositionTraits] = None
|
|
583
583
|
background: Optional[str] = None
|
|
584
584
|
|
|
585
585
|
|
|
@@ -833,7 +833,7 @@ def _register_routes(app: FastAPI):
|
|
|
833
833
|
"/v1/default/banks/{bank_id}/graph",
|
|
834
834
|
response_model=GraphDataResponse,
|
|
835
835
|
summary="Get memory graph data",
|
|
836
|
-
description="Retrieve graph data for visualization, optionally filtered by type (world/
|
|
836
|
+
description="Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion). Limited to 1000 most recent items.",
|
|
837
837
|
operation_id="get_graph"
|
|
838
838
|
)
|
|
839
839
|
async def api_graph(bank_id: str,
|
|
@@ -871,7 +871,7 @@ def _register_routes(app: FastAPI):
|
|
|
871
871
|
|
|
872
872
|
Args:
|
|
873
873
|
bank_id: Memory Bank ID (from path)
|
|
874
|
-
type: Filter by fact type (world,
|
|
874
|
+
type: Filter by fact type (world, experience, opinion)
|
|
875
875
|
q: Search query for full-text search (searches text and context)
|
|
876
876
|
limit: Maximum number of results (default: 100)
|
|
877
877
|
offset: Offset for pagination (default: 0)
|
|
@@ -901,7 +901,7 @@ def _register_routes(app: FastAPI):
|
|
|
901
901
|
|
|
902
902
|
The type parameter is optional and must be one of:
|
|
903
903
|
- 'world': General knowledge about people, places, events, and things that happen
|
|
904
|
-
- '
|
|
904
|
+
- 'experience': Memories about experience, conversations, actions taken, and tasks performed
|
|
905
905
|
- 'opinion': The bank's formed beliefs, perspectives, and viewpoints
|
|
906
906
|
|
|
907
907
|
Set include_entities=true to get entity observations alongside recall results.
|
|
@@ -914,10 +914,10 @@ def _register_routes(app: FastAPI):
|
|
|
914
914
|
|
|
915
915
|
try:
|
|
916
916
|
# Validate types
|
|
917
|
-
valid_fact_types = ["world", "
|
|
917
|
+
valid_fact_types = ["world", "experience", "opinion"]
|
|
918
918
|
|
|
919
|
-
# Default to world,
|
|
920
|
-
fact_types = request.types if request.types else ["world", "
|
|
919
|
+
# Default to world, experience, opinion if not specified (exclude observation by default)
|
|
920
|
+
fact_types = request.types if request.types else ["world", "experience", "opinion"]
|
|
921
921
|
for ft in fact_types:
|
|
922
922
|
if ft not in valid_fact_types:
|
|
923
923
|
raise HTTPException(
|
|
@@ -1026,7 +1026,7 @@ def _register_routes(app: FastAPI):
|
|
|
1026
1026
|
Reflect and formulate an answer using bank identity, world facts, and opinions.
|
|
1027
1027
|
|
|
1028
1028
|
This endpoint:
|
|
1029
|
-
1. Retrieves
|
|
1029
|
+
1. Retrieves experience (conversations and events)
|
|
1030
1030
|
2. Retrieves world facts relevant to the query
|
|
1031
1031
|
3. Retrieves existing opinions (bank's perspectives)
|
|
1032
1032
|
4. Uses LLM to formulate a contextual answer
|
|
@@ -1579,19 +1579,19 @@ This operation cannot be undone.
|
|
|
1579
1579
|
"/v1/default/banks/{bank_id}/profile",
|
|
1580
1580
|
response_model=BankProfileResponse,
|
|
1581
1581
|
summary="Get memory bank profile",
|
|
1582
|
-
description="Get
|
|
1582
|
+
description="Get disposition traits and background for a memory bank. Auto-creates agent with defaults if not exists.",
|
|
1583
1583
|
operation_id="get_bank_profile"
|
|
1584
1584
|
)
|
|
1585
1585
|
async def api_get_bank_profile(bank_id: str):
|
|
1586
|
-
"""Get memory bank profile (
|
|
1586
|
+
"""Get memory bank profile (disposition + background)."""
|
|
1587
1587
|
try:
|
|
1588
1588
|
profile = await app.state.memory.get_bank_profile(bank_id)
|
|
1589
|
-
# Convert
|
|
1590
|
-
|
|
1589
|
+
# Convert DispositionTraits object to dict for Pydantic
|
|
1590
|
+
disposition_dict = profile["disposition"].model_dump() if hasattr(profile["disposition"], 'model_dump') else dict(profile["disposition"])
|
|
1591
1591
|
return BankProfileResponse(
|
|
1592
1592
|
bank_id=bank_id,
|
|
1593
1593
|
name=profile["name"],
|
|
1594
|
-
|
|
1594
|
+
disposition=DispositionTraits(**disposition_dict),
|
|
1595
1595
|
background=profile["background"]
|
|
1596
1596
|
)
|
|
1597
1597
|
except Exception as e:
|
|
@@ -1604,28 +1604,28 @@ This operation cannot be undone.
|
|
|
1604
1604
|
@app.put(
|
|
1605
1605
|
"/v1/default/banks/{bank_id}/profile",
|
|
1606
1606
|
response_model=BankProfileResponse,
|
|
1607
|
-
summary="Update memory bank
|
|
1608
|
-
description="Update bank's Big Five
|
|
1609
|
-
operation_id="
|
|
1607
|
+
summary="Update memory bank disposition",
|
|
1608
|
+
description="Update bank's Big Five disposition traits and bias strength",
|
|
1609
|
+
operation_id="update_bank_disposition"
|
|
1610
1610
|
)
|
|
1611
|
-
async def
|
|
1612
|
-
request:
|
|
1611
|
+
async def api_update_bank_disposition(bank_id: str,
|
|
1612
|
+
request: UpdateDispositionRequest
|
|
1613
1613
|
):
|
|
1614
|
-
"""Update bank
|
|
1614
|
+
"""Update bank disposition traits."""
|
|
1615
1615
|
try:
|
|
1616
|
-
# Update
|
|
1617
|
-
await app.state.memory.
|
|
1616
|
+
# Update disposition
|
|
1617
|
+
await app.state.memory.update_bank_disposition(
|
|
1618
1618
|
bank_id,
|
|
1619
|
-
request.
|
|
1619
|
+
request.disposition.model_dump()
|
|
1620
1620
|
)
|
|
1621
1621
|
|
|
1622
1622
|
# Get updated profile
|
|
1623
1623
|
profile = await app.state.memory.get_bank_profile(bank_id)
|
|
1624
|
-
|
|
1624
|
+
disposition_dict = profile["disposition"].model_dump() if hasattr(profile["disposition"], 'model_dump') else dict(profile["disposition"])
|
|
1625
1625
|
return BankProfileResponse(
|
|
1626
1626
|
bank_id=bank_id,
|
|
1627
1627
|
name=profile["name"],
|
|
1628
|
-
|
|
1628
|
+
disposition=DispositionTraits(**disposition_dict),
|
|
1629
1629
|
background=profile["background"]
|
|
1630
1630
|
)
|
|
1631
1631
|
except Exception as e:
|
|
@@ -1639,23 +1639,23 @@ This operation cannot be undone.
|
|
|
1639
1639
|
"/v1/default/banks/{bank_id}/background",
|
|
1640
1640
|
response_model=BackgroundResponse,
|
|
1641
1641
|
summary="Add/merge memory bank background",
|
|
1642
|
-
description="Add new background information or merge with existing. LLM intelligently resolves conflicts, normalizes to first person, and optionally infers
|
|
1642
|
+
description="Add new background information or merge with existing. LLM intelligently resolves conflicts, normalizes to first person, and optionally infers disposition traits.",
|
|
1643
1643
|
operation_id="add_bank_background"
|
|
1644
1644
|
)
|
|
1645
1645
|
async def api_add_bank_background(bank_id: str,
|
|
1646
1646
|
request: AddBackgroundRequest
|
|
1647
1647
|
):
|
|
1648
|
-
"""Add or merge bank background information. Optionally infer
|
|
1648
|
+
"""Add or merge bank background information. Optionally infer disposition traits."""
|
|
1649
1649
|
try:
|
|
1650
1650
|
result = await app.state.memory.merge_bank_background(
|
|
1651
1651
|
bank_id,
|
|
1652
1652
|
request.content,
|
|
1653
|
-
|
|
1653
|
+
update_disposition=request.update_disposition
|
|
1654
1654
|
)
|
|
1655
1655
|
|
|
1656
1656
|
response = BackgroundResponse(background=result["background"])
|
|
1657
|
-
if "
|
|
1658
|
-
response.
|
|
1657
|
+
if "disposition" in result:
|
|
1658
|
+
response.disposition = DispositionTraits(**result["disposition"])
|
|
1659
1659
|
|
|
1660
1660
|
return response
|
|
1661
1661
|
except Exception as e:
|
|
@@ -1669,13 +1669,13 @@ This operation cannot be undone.
|
|
|
1669
1669
|
"/v1/default/banks/{bank_id}",
|
|
1670
1670
|
response_model=BankProfileResponse,
|
|
1671
1671
|
summary="Create or update memory bank",
|
|
1672
|
-
description="Create a new agent or update existing agent with
|
|
1672
|
+
description="Create a new agent or update existing agent with disposition and background. Auto-fills missing fields with defaults.",
|
|
1673
1673
|
operation_id="create_or_update_bank"
|
|
1674
1674
|
)
|
|
1675
1675
|
async def api_create_or_update_bank(bank_id: str,
|
|
1676
1676
|
request: CreateBankRequest
|
|
1677
1677
|
):
|
|
1678
|
-
"""Create or update an agent with
|
|
1678
|
+
"""Create or update an agent with disposition and background."""
|
|
1679
1679
|
try:
|
|
1680
1680
|
# Get existing profile or create with defaults
|
|
1681
1681
|
profile = await app.state.memory.get_bank_profile(bank_id)
|
|
@@ -1696,13 +1696,13 @@ This operation cannot be undone.
|
|
|
1696
1696
|
)
|
|
1697
1697
|
profile["name"] = request.name
|
|
1698
1698
|
|
|
1699
|
-
# Update
|
|
1700
|
-
if request.
|
|
1701
|
-
await app.state.memory.
|
|
1699
|
+
# Update disposition if provided
|
|
1700
|
+
if request.disposition is not None:
|
|
1701
|
+
await app.state.memory.update_bank_disposition(
|
|
1702
1702
|
bank_id,
|
|
1703
|
-
request.
|
|
1703
|
+
request.disposition.model_dump()
|
|
1704
1704
|
)
|
|
1705
|
-
profile["
|
|
1705
|
+
profile["disposition"] = request.disposition.model_dump()
|
|
1706
1706
|
|
|
1707
1707
|
# Update background if provided (replace, not merge)
|
|
1708
1708
|
if request.background is not None:
|
|
@@ -1722,11 +1722,11 @@ This operation cannot be undone.
|
|
|
1722
1722
|
|
|
1723
1723
|
# Get final profile
|
|
1724
1724
|
final_profile = await app.state.memory.get_bank_profile(bank_id)
|
|
1725
|
-
|
|
1725
|
+
disposition_dict = final_profile["disposition"].model_dump() if hasattr(final_profile["disposition"], 'model_dump') else dict(final_profile["disposition"])
|
|
1726
1726
|
return BankProfileResponse(
|
|
1727
1727
|
bank_id=bank_id,
|
|
1728
1728
|
name=final_profile["name"],
|
|
1729
|
-
|
|
1729
|
+
disposition=DispositionTraits(**disposition_dict),
|
|
1730
1730
|
background=final_profile["background"]
|
|
1731
1731
|
)
|
|
1732
1732
|
except Exception as e:
|
|
@@ -1852,11 +1852,11 @@ This operation cannot be undone.
|
|
|
1852
1852
|
"/v1/default/banks/{bank_id}/memories",
|
|
1853
1853
|
response_model=DeleteResponse,
|
|
1854
1854
|
summary="Clear memory bank memories",
|
|
1855
|
-
description="Delete memory units for a memory bank. Optionally filter by type (world,
|
|
1855
|
+
description="Delete memory units for a memory bank. Optionally filter by type (world, experience, opinion) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (personality and background) will be preserved.",
|
|
1856
1856
|
operation_id="clear_bank_memories"
|
|
1857
1857
|
)
|
|
1858
1858
|
async def api_clear_bank_memories(bank_id: str,
|
|
1859
|
-
type: Optional[str] = Query(None, description="Optional fact type filter (world,
|
|
1859
|
+
type: Optional[str] = Query(None, description="Optional fact type filter (world, experience, opinion)")
|
|
1860
1860
|
):
|
|
1861
1861
|
"""Clear memories for a memory bank, optionally filtered by type."""
|
|
1862
1862
|
try:
|