hindsight-api 0.1.11__tar.gz → 0.1.13__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.
Files changed (76) hide show
  1. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/PKG-INFO +1 -1
  2. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/__init__.py +2 -0
  3. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/env.py +24 -1
  4. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/versions/d9f6a3b4c5e2_rename_bank_to_interactions.py +14 -4
  5. hindsight_api-0.1.13/hindsight_api/alembic/versions/e0a1b2c3d4e5_disposition_to_3_traits.py +111 -0
  6. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/versions/rename_personality_to_disposition.py +18 -7
  7. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/api/http.py +253 -230
  8. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/api/mcp.py +14 -3
  9. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/config.py +11 -0
  10. hindsight_api-0.1.13/hindsight_api/daemon.py +204 -0
  11. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/__init__.py +12 -1
  12. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/entity_resolver.py +38 -37
  13. hindsight_api-0.1.13/hindsight_api/engine/interface.py +592 -0
  14. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/llm_wrapper.py +176 -6
  15. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/memory_engine.py +1092 -293
  16. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/bank_utils.py +13 -12
  17. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/chunk_storage.py +3 -2
  18. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/fact_storage.py +10 -7
  19. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/link_utils.py +17 -16
  20. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/observation_regeneration.py +17 -16
  21. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/orchestrator.py +2 -3
  22. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/types.py +25 -8
  23. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/graph_retrieval.py +6 -5
  24. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/mpfp_retrieval.py +8 -7
  25. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/reranking.py +17 -0
  26. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/retrieval.py +12 -11
  27. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/think_utils.py +1 -1
  28. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/tracer.py +1 -1
  29. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/task_backend.py +32 -0
  30. hindsight_api-0.1.13/hindsight_api/extensions/__init__.py +66 -0
  31. hindsight_api-0.1.13/hindsight_api/extensions/base.py +81 -0
  32. hindsight_api-0.1.13/hindsight_api/extensions/builtin/__init__.py +18 -0
  33. hindsight_api-0.1.13/hindsight_api/extensions/builtin/tenant.py +33 -0
  34. hindsight_api-0.1.13/hindsight_api/extensions/context.py +110 -0
  35. hindsight_api-0.1.13/hindsight_api/extensions/http.py +89 -0
  36. hindsight_api-0.1.13/hindsight_api/extensions/loader.py +125 -0
  37. hindsight_api-0.1.13/hindsight_api/extensions/operation_validator.py +325 -0
  38. hindsight_api-0.1.13/hindsight_api/extensions/tenant.py +63 -0
  39. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/main.py +97 -17
  40. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/mcp_local.py +7 -1
  41. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/migrations.py +54 -10
  42. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/models.py +15 -0
  43. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/pg0.py +1 -1
  44. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/pyproject.toml +27 -1
  45. hindsight_api-0.1.11/hindsight_api/alembic/versions/e0a1b2c3d4e5_disposition_to_3_traits.py +0 -70
  46. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/.gitignore +0 -0
  47. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/README.md +0 -0
  48. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/README +0 -0
  49. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/script.py.mako +0 -0
  50. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/versions/5a366d414dce_initial_schema.py +0 -0
  51. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/versions/b7c4d8e9f1a2_add_chunks_table.py +0 -0
  52. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/alembic/versions/c8e5f2a3b4d1_add_retain_params_to_documents.py +0 -0
  53. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/api/__init__.py +0 -0
  54. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/banner.py +0 -0
  55. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/cross_encoder.py +0 -0
  56. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/db_utils.py +0 -0
  57. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/embeddings.py +0 -0
  58. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/query_analyzer.py +0 -0
  59. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/response_models.py +0 -0
  60. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/__init__.py +0 -0
  61. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/deduplication.py +0 -0
  62. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/embedding_processing.py +0 -0
  63. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/embedding_utils.py +0 -0
  64. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/entity_processing.py +0 -0
  65. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/fact_extraction.py +0 -0
  66. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/retain/link_creation.py +0 -0
  67. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/__init__.py +0 -0
  68. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/fusion.py +0 -0
  69. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/observation_utils.py +0 -0
  70. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/scoring.py +0 -0
  71. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/temporal_extraction.py +0 -0
  72. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/trace.py +0 -0
  73. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/search/types.py +0 -0
  74. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/engine/utils.py +0 -0
  75. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/metrics.py +0 -0
  76. {hindsight_api-0.1.11 → hindsight_api-0.1.13}/hindsight_api/server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hindsight-api
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Hindsight: Agent Memory That Works Like Human Memory
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: alembic>=1.17.1
@@ -21,9 +21,11 @@ from .engine.search.trace import (
21
21
  WeightComponents,
22
22
  )
23
23
  from .engine.search.tracer import SearchTracer
24
+ from .models import RequestContext
24
25
 
25
26
  __all__ = [
26
27
  "MemoryEngine",
28
+ "RequestContext",
27
29
  "HindsightConfig",
28
30
  "get_config",
29
31
  "SearchTrace",
@@ -109,6 +109,9 @@ def run_migrations_online() -> None:
109
109
 
110
110
  get_database_url() # Process and set the database URL in config
111
111
 
112
+ # Check if we're targeting a specific schema (for multi-tenant isolation)
113
+ target_schema = config.get_main_option("target_schema")
114
+
112
115
  connectable = engine_from_config(
113
116
  config.get_section(config.config_ini_section, {}),
114
117
  prefix="sqlalchemy.",
@@ -121,14 +124,34 @@ def run_migrations_online() -> None:
121
124
  def set_read_write_mode(dbapi_connection, connection_record):
122
125
  cursor = dbapi_connection.cursor()
123
126
  cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE")
127
+ # If targeting a specific schema, set search_path
128
+ # Include public in search_path for access to shared extensions (pgvector)
129
+ if target_schema:
130
+ cursor.execute(f'CREATE SCHEMA IF NOT EXISTS "{target_schema}"')
131
+ cursor.execute(f'SET search_path TO "{target_schema}", public')
124
132
  cursor.close()
125
133
 
126
134
  with connectable.connect() as connection:
127
135
  # Also explicitly set read-write mode on this connection
128
136
  connection.execute(text("SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE"))
137
+
138
+ # If targeting a specific schema, set search_path
139
+ # Include public in search_path for access to shared extensions (pgvector)
140
+ if target_schema:
141
+ connection.execute(text(f'CREATE SCHEMA IF NOT EXISTS "{target_schema}"'))
142
+ connection.execute(text(f'SET search_path TO "{target_schema}", public'))
143
+
129
144
  connection.commit() # Commit the SET command
130
145
 
131
- context.configure(connection=connection, target_metadata=target_metadata)
146
+ # Configure context with version_table_schema if using a specific schema
147
+ context_opts = {
148
+ "connection": connection,
149
+ "target_metadata": target_metadata,
150
+ }
151
+ if target_schema:
152
+ context_opts["version_table_schema"] = target_schema
153
+
154
+ context.configure(**context_opts)
132
155
 
133
156
  with context.begin_transaction():
134
157
  context.run_migrations()
@@ -6,7 +6,7 @@ Create Date: 2024-12-04 15:00:00.000000
6
6
 
7
7
  """
8
8
 
9
- from alembic import op
9
+ from alembic import context, op
10
10
 
11
11
  # revision identifiers, used by Alembic.
12
12
  revision = "d9f6a3b4c5e2"
@@ -15,14 +15,22 @@ branch_labels = None
15
15
  depends_on = None
16
16
 
17
17
 
18
+ def _get_schema_prefix() -> str:
19
+ """Get schema prefix for table names (e.g., 'tenant_x.' or '' for public)."""
20
+ schema = context.config.get_main_option("target_schema")
21
+ return f'"{schema}".' if schema else ""
22
+
23
+
18
24
  def upgrade():
25
+ schema = _get_schema_prefix()
26
+
19
27
  # Drop old check constraint FIRST (before updating data)
20
28
  op.drop_constraint("memory_units_fact_type_check", "memory_units", type_="check")
21
29
 
22
30
  # Update existing 'bank' values to 'experience'
23
- op.execute("UPDATE memory_units SET fact_type = 'experience' WHERE fact_type = 'bank'")
31
+ op.execute(f"UPDATE {schema}memory_units SET fact_type = 'experience' WHERE fact_type = 'bank'")
24
32
  # Also update any 'interactions' values (in case of partial migration)
25
- op.execute("UPDATE memory_units SET fact_type = 'experience' WHERE fact_type = 'interactions'")
33
+ op.execute(f"UPDATE {schema}memory_units SET fact_type = 'experience' WHERE fact_type = 'interactions'")
26
34
 
27
35
  # Create new check constraint with 'experience' instead of 'bank'
28
36
  op.create_check_constraint(
@@ -31,11 +39,13 @@ def upgrade():
31
39
 
32
40
 
33
41
  def downgrade():
42
+ schema = _get_schema_prefix()
43
+
34
44
  # Drop new check constraint FIRST
35
45
  op.drop_constraint("memory_units_fact_type_check", "memory_units", type_="check")
36
46
 
37
47
  # Update 'experience' back to 'bank'
38
- op.execute("UPDATE memory_units SET fact_type = 'bank' WHERE fact_type = 'experience'")
48
+ op.execute(f"UPDATE {schema}memory_units SET fact_type = 'bank' WHERE fact_type = 'experience'")
39
49
 
40
50
  # Recreate old check constraint
41
51
  op.create_check_constraint(
@@ -0,0 +1,111 @@
1
+ """disposition_to_3_traits
2
+
3
+ Revision ID: e0a1b2c3d4e5
4
+ Revises: rename_personality
5
+ Create Date: 2024-12-08
6
+
7
+ Migrate disposition traits from Big Five (openness, conscientiousness, extraversion,
8
+ agreeableness, neuroticism, bias_strength with 0-1 float values) to the new 3-trait
9
+ system (skepticism, literalism, empathy with 1-5 integer values).
10
+ """
11
+
12
+ from collections.abc import Sequence
13
+
14
+ import sqlalchemy as sa
15
+ from alembic import context, op
16
+
17
+ # revision identifiers, used by Alembic.
18
+ revision: str = "e0a1b2c3d4e5"
19
+ down_revision: str | Sequence[str] | None = "rename_personality"
20
+ branch_labels: str | Sequence[str] | None = None
21
+ depends_on: str | Sequence[str] | None = None
22
+
23
+
24
+ def _get_schema_prefix() -> str:
25
+ """Get schema prefix for table names (e.g., 'tenant_x.' or '' for public)."""
26
+ schema = context.config.get_main_option("target_schema")
27
+ return f'"{schema}".' if schema else ""
28
+
29
+
30
+ def _get_target_schema() -> str:
31
+ """Get the target schema name (tenant schema or 'public')."""
32
+ schema = context.config.get_main_option("target_schema")
33
+ return schema if schema else "public"
34
+
35
+
36
+ def upgrade() -> None:
37
+ """Convert Big Five disposition to 3-trait disposition."""
38
+ conn = op.get_bind()
39
+ schema = _get_schema_prefix()
40
+ target_schema = _get_target_schema()
41
+
42
+ # Check if disposition column exists (should have been created by previous migration)
43
+ result = conn.execute(
44
+ sa.text("""
45
+ SELECT column_name
46
+ FROM information_schema.columns
47
+ WHERE table_schema = :schema AND table_name = 'banks' AND column_name = 'disposition'
48
+ """),
49
+ {"schema": target_schema},
50
+ )
51
+ if not result.fetchone():
52
+ # Column doesn't exist yet (shouldn't happen but be safe)
53
+ return
54
+
55
+ # Update all existing banks to use the new disposition format
56
+ # Convert from old format to new format with reasonable mappings:
57
+ # - skepticism: derived from inverse of agreeableness (skeptical people are less agreeable)
58
+ # - literalism: derived from conscientiousness (detail-oriented people are more literal)
59
+ # - empathy: derived from agreeableness + inverse of neuroticism
60
+ # Default all to 3 (neutral) for simplicity
61
+ conn.execute(
62
+ sa.text(f"""
63
+ UPDATE {schema}banks
64
+ SET disposition = '{{"skepticism": 3, "literalism": 3, "empathy": 3}}'::jsonb
65
+ WHERE disposition IS NOT NULL
66
+ """)
67
+ )
68
+
69
+ # Update the default for new banks
70
+ conn.execute(
71
+ sa.text(f"""
72
+ ALTER TABLE {schema}banks
73
+ ALTER COLUMN disposition SET DEFAULT '{{"skepticism": 3, "literalism": 3, "empathy": 3}}'::jsonb
74
+ """)
75
+ )
76
+
77
+
78
+ def downgrade() -> None:
79
+ """Convert back to Big Five disposition."""
80
+ conn = op.get_bind()
81
+ schema = _get_schema_prefix()
82
+ target_schema = _get_target_schema()
83
+
84
+ # Check if disposition column exists
85
+ result = conn.execute(
86
+ sa.text("""
87
+ SELECT column_name
88
+ FROM information_schema.columns
89
+ WHERE table_schema = :schema AND table_name = 'banks' AND column_name = 'disposition'
90
+ """),
91
+ {"schema": target_schema},
92
+ )
93
+ if not result.fetchone():
94
+ return
95
+
96
+ # Revert to Big Five format with default values
97
+ conn.execute(
98
+ sa.text(f"""
99
+ UPDATE {schema}banks
100
+ SET disposition = '{{"openness": 0.5, "conscientiousness": 0.5, "extraversion": 0.5, "agreeableness": 0.5, "neuroticism": 0.5, "bias_strength": 0.5}}'::jsonb
101
+ WHERE disposition IS NOT NULL
102
+ """)
103
+ )
104
+
105
+ # Update the default for new banks
106
+ conn.execute(
107
+ sa.text(f"""
108
+ ALTER TABLE {schema}banks
109
+ ALTER COLUMN disposition SET DEFAULT '{{"openness": 0.5, "conscientiousness": 0.5, "extraversion": 0.5, "agreeableness": 0.5, "neuroticism": 0.5, "bias_strength": 0.5}}'::jsonb
110
+ """)
111
+ )
@@ -9,7 +9,7 @@ Create Date: 2024-12-04
9
9
  from collections.abc import Sequence
10
10
 
11
11
  import sqlalchemy as sa
12
- from alembic import op
12
+ from alembic import context, op
13
13
  from sqlalchemy.dialects import postgresql
14
14
 
15
15
  # revision identifiers, used by Alembic.
@@ -19,17 +19,25 @@ branch_labels: str | Sequence[str] | None = None
19
19
  depends_on: str | Sequence[str] | None = None
20
20
 
21
21
 
22
+ def _get_target_schema() -> str:
23
+ """Get the target schema name (tenant schema or 'public')."""
24
+ schema = context.config.get_main_option("target_schema")
25
+ return schema if schema else "public"
26
+
27
+
22
28
  def upgrade() -> None:
23
29
  """Rename personality column to disposition in banks table (if it exists)."""
24
30
  conn = op.get_bind()
31
+ target_schema = _get_target_schema()
25
32
 
26
33
  # Check if 'personality' column exists (old database)
27
34
  result = conn.execute(
28
35
  sa.text("""
29
36
  SELECT column_name
30
37
  FROM information_schema.columns
31
- WHERE table_name = 'banks' AND column_name = 'personality'
32
- """)
38
+ WHERE table_schema = :schema AND table_name = 'banks' AND column_name = 'personality'
39
+ """),
40
+ {"schema": target_schema},
33
41
  )
34
42
  has_personality = result.fetchone() is not None
35
43
 
@@ -38,8 +46,9 @@ def upgrade() -> None:
38
46
  sa.text("""
39
47
  SELECT column_name
40
48
  FROM information_schema.columns
41
- WHERE table_name = 'banks' AND column_name = 'disposition'
42
- """)
49
+ WHERE table_schema = :schema AND table_name = 'banks' AND column_name = 'disposition'
50
+ """),
51
+ {"schema": target_schema},
43
52
  )
44
53
  has_disposition = result.fetchone() is not None
45
54
 
@@ -63,12 +72,14 @@ def upgrade() -> None:
63
72
  def downgrade() -> None:
64
73
  """Revert disposition column back to personality."""
65
74
  conn = op.get_bind()
75
+ target_schema = _get_target_schema()
66
76
  result = conn.execute(
67
77
  sa.text("""
68
78
  SELECT column_name
69
79
  FROM information_schema.columns
70
- WHERE table_name = 'banks' AND column_name = 'disposition'
71
- """)
80
+ WHERE table_schema = :schema AND table_name = 'banks' AND column_name = 'disposition'
81
+ """),
82
+ {"schema": target_schema},
72
83
  )
73
84
  if result.fetchone():
74
85
  op.alter_column("banks", "disposition", new_column_name="personality")