hindsight-api 0.2.1__py3-none-any.whl → 0.4.0__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.
- hindsight_api/admin/__init__.py +1 -0
- hindsight_api/admin/cli.py +311 -0
- hindsight_api/alembic/versions/f1a2b3c4d5e6_add_memory_links_composite_index.py +44 -0
- hindsight_api/alembic/versions/g2a3b4c5d6e7_add_tags_column.py +48 -0
- hindsight_api/alembic/versions/h3c4d5e6f7g8_mental_models_v4.py +112 -0
- hindsight_api/alembic/versions/i4d5e6f7g8h9_delete_opinions.py +41 -0
- hindsight_api/alembic/versions/j5e6f7g8h9i0_mental_model_versions.py +95 -0
- hindsight_api/alembic/versions/k6f7g8h9i0j1_add_directive_subtype.py +58 -0
- hindsight_api/alembic/versions/l7g8h9i0j1k2_add_worker_columns.py +109 -0
- hindsight_api/alembic/versions/m8h9i0j1k2l3_mental_model_id_to_text.py +41 -0
- hindsight_api/alembic/versions/n9i0j1k2l3m4_learnings_and_pinned_reflections.py +134 -0
- hindsight_api/alembic/versions/o0j1k2l3m4n5_migrate_mental_models_data.py +113 -0
- hindsight_api/alembic/versions/p1k2l3m4n5o6_new_knowledge_architecture.py +194 -0
- hindsight_api/alembic/versions/q2l3m4n5o6p7_fix_mental_model_fact_type.py +50 -0
- hindsight_api/alembic/versions/r3m4n5o6p7q8_add_reflect_response_to_reflections.py +47 -0
- hindsight_api/alembic/versions/s4n5o6p7q8r9_add_consolidated_at_to_memory_units.py +53 -0
- hindsight_api/alembic/versions/t5o6p7q8r9s0_rename_mental_models_to_observations.py +134 -0
- hindsight_api/alembic/versions/u6p7q8r9s0t1_mental_models_text_id.py +41 -0
- hindsight_api/alembic/versions/v7q8r9s0t1u2_add_max_tokens_to_mental_models.py +50 -0
- hindsight_api/api/http.py +1406 -118
- hindsight_api/api/mcp.py +11 -196
- hindsight_api/config.py +359 -27
- hindsight_api/engine/consolidation/__init__.py +5 -0
- hindsight_api/engine/consolidation/consolidator.py +859 -0
- hindsight_api/engine/consolidation/prompts.py +69 -0
- hindsight_api/engine/cross_encoder.py +706 -88
- hindsight_api/engine/db_budget.py +284 -0
- hindsight_api/engine/db_utils.py +11 -0
- hindsight_api/engine/directives/__init__.py +5 -0
- hindsight_api/engine/directives/models.py +37 -0
- hindsight_api/engine/embeddings.py +553 -29
- hindsight_api/engine/entity_resolver.py +8 -5
- hindsight_api/engine/interface.py +40 -17
- hindsight_api/engine/llm_wrapper.py +744 -68
- hindsight_api/engine/memory_engine.py +2505 -1017
- hindsight_api/engine/mental_models/__init__.py +14 -0
- hindsight_api/engine/mental_models/models.py +53 -0
- hindsight_api/engine/query_analyzer.py +4 -3
- hindsight_api/engine/reflect/__init__.py +18 -0
- hindsight_api/engine/reflect/agent.py +933 -0
- hindsight_api/engine/reflect/models.py +109 -0
- hindsight_api/engine/reflect/observations.py +186 -0
- hindsight_api/engine/reflect/prompts.py +483 -0
- hindsight_api/engine/reflect/tools.py +437 -0
- hindsight_api/engine/reflect/tools_schema.py +250 -0
- hindsight_api/engine/response_models.py +168 -4
- hindsight_api/engine/retain/bank_utils.py +79 -201
- hindsight_api/engine/retain/fact_extraction.py +424 -195
- hindsight_api/engine/retain/fact_storage.py +35 -12
- hindsight_api/engine/retain/link_utils.py +29 -24
- hindsight_api/engine/retain/orchestrator.py +24 -43
- hindsight_api/engine/retain/types.py +11 -2
- hindsight_api/engine/search/graph_retrieval.py +43 -14
- hindsight_api/engine/search/link_expansion_retrieval.py +391 -0
- hindsight_api/engine/search/mpfp_retrieval.py +362 -117
- hindsight_api/engine/search/reranking.py +2 -2
- hindsight_api/engine/search/retrieval.py +848 -201
- hindsight_api/engine/search/tags.py +172 -0
- hindsight_api/engine/search/think_utils.py +42 -141
- hindsight_api/engine/search/trace.py +12 -1
- hindsight_api/engine/search/tracer.py +26 -6
- hindsight_api/engine/search/types.py +21 -3
- hindsight_api/engine/task_backend.py +113 -106
- hindsight_api/engine/utils.py +1 -152
- hindsight_api/extensions/__init__.py +10 -1
- hindsight_api/extensions/builtin/tenant.py +5 -1
- hindsight_api/extensions/context.py +10 -1
- hindsight_api/extensions/operation_validator.py +81 -4
- hindsight_api/extensions/tenant.py +26 -0
- hindsight_api/main.py +69 -6
- hindsight_api/mcp_local.py +12 -53
- hindsight_api/mcp_tools.py +494 -0
- hindsight_api/metrics.py +433 -48
- hindsight_api/migrations.py +141 -1
- hindsight_api/models.py +3 -3
- hindsight_api/pg0.py +53 -0
- hindsight_api/server.py +39 -2
- hindsight_api/worker/__init__.py +11 -0
- hindsight_api/worker/main.py +296 -0
- hindsight_api/worker/poller.py +486 -0
- {hindsight_api-0.2.1.dist-info → hindsight_api-0.4.0.dist-info}/METADATA +16 -6
- hindsight_api-0.4.0.dist-info/RECORD +112 -0
- {hindsight_api-0.2.1.dist-info → hindsight_api-0.4.0.dist-info}/entry_points.txt +2 -0
- hindsight_api/engine/retain/observation_regeneration.py +0 -254
- hindsight_api/engine/search/observation_utils.py +0 -125
- hindsight_api/engine/search/scoring.py +0 -159
- hindsight_api-0.2.1.dist-info/RECORD +0 -75
- {hindsight_api-0.2.1.dist-info → hindsight_api-0.4.0.dist-info}/WHEEL +0 -0
hindsight_api/api/mcp.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Hindsight MCP Server implementation using FastMCP."""
|
|
1
|
+
"""Hindsight MCP Server implementation using FastMCP (HTTP transport)."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
@@ -8,9 +8,7 @@ from contextvars import ContextVar
|
|
|
8
8
|
from fastmcp import FastMCP
|
|
9
9
|
|
|
10
10
|
from hindsight_api import MemoryEngine
|
|
11
|
-
from hindsight_api.
|
|
12
|
-
from hindsight_api.engine.response_models import VALID_RECALL_FACT_TYPES
|
|
13
|
-
from hindsight_api.models import RequestContext
|
|
11
|
+
from hindsight_api.mcp_tools import MCPToolsConfig, register_mcp_tools
|
|
14
12
|
|
|
15
13
|
# Configure logging from HINDSIGHT_API_LOG_LEVEL environment variable
|
|
16
14
|
_log_level_str = os.environ.get("HINDSIGHT_API_LOG_LEVEL", "info").lower()
|
|
@@ -53,198 +51,15 @@ def create_mcp_server(memory: MemoryEngine) -> FastMCP:
|
|
|
53
51
|
# Use stateless_http=True for Claude Code compatibility
|
|
54
52
|
mcp = FastMCP("hindsight-mcp-server", stateless_http=True)
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- Decisions, opinions, or stated preferences
|
|
66
|
-
- Goals, plans, or future intentions
|
|
67
|
-
- Relationships or people mentioned
|
|
68
|
-
- Work context, projects, or responsibilities
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
content: The fact/memory to store (be specific and include relevant details)
|
|
72
|
-
context: Category for the memory (e.g., 'preferences', 'work', 'hobbies', 'family'). Default: 'general'
|
|
73
|
-
bank_id: Optional bank to store in (defaults to session bank). Use for cross-bank operations.
|
|
74
|
-
"""
|
|
75
|
-
try:
|
|
76
|
-
target_bank = bank_id or get_current_bank_id()
|
|
77
|
-
if target_bank is None:
|
|
78
|
-
return "Error: No bank_id configured"
|
|
79
|
-
await memory.retain_batch_async(
|
|
80
|
-
bank_id=target_bank,
|
|
81
|
-
contents=[{"content": content, "context": context}],
|
|
82
|
-
request_context=RequestContext(),
|
|
83
|
-
)
|
|
84
|
-
return f"Memory stored successfully in bank '{target_bank}'"
|
|
85
|
-
except Exception as e:
|
|
86
|
-
logger.error(f"Error storing memory: {e}", exc_info=True)
|
|
87
|
-
return f"Error: {str(e)}"
|
|
88
|
-
|
|
89
|
-
@mcp.tool()
|
|
90
|
-
async def recall(query: str, max_tokens: int = 4096, bank_id: str | None = None) -> str:
|
|
91
|
-
"""
|
|
92
|
-
Search memories to provide personalized, context-aware responses.
|
|
93
|
-
|
|
94
|
-
Use this tool PROACTIVELY to:
|
|
95
|
-
- Check user's preferences before making suggestions
|
|
96
|
-
- Recall user's history to provide continuity
|
|
97
|
-
- Remember user's goals and context
|
|
98
|
-
- Personalize responses based on past interactions
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
query: Natural language search query (e.g., "user's food preferences", "what projects is user working on")
|
|
102
|
-
max_tokens: Maximum tokens in the response (default: 4096)
|
|
103
|
-
bank_id: Optional bank to search in (defaults to session bank). Use for cross-bank operations.
|
|
104
|
-
"""
|
|
105
|
-
try:
|
|
106
|
-
target_bank = bank_id or get_current_bank_id()
|
|
107
|
-
if target_bank is None:
|
|
108
|
-
return "Error: No bank_id configured"
|
|
109
|
-
from hindsight_api.engine.memory_engine import Budget
|
|
110
|
-
|
|
111
|
-
recall_result = await memory.recall_async(
|
|
112
|
-
bank_id=target_bank,
|
|
113
|
-
query=query,
|
|
114
|
-
fact_type=list(VALID_RECALL_FACT_TYPES),
|
|
115
|
-
budget=Budget.HIGH,
|
|
116
|
-
max_tokens=max_tokens,
|
|
117
|
-
request_context=RequestContext(),
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
# Use model's JSON serialization
|
|
121
|
-
return recall_result.model_dump_json(indent=2)
|
|
122
|
-
except Exception as e:
|
|
123
|
-
logger.error(f"Error searching: {e}", exc_info=True)
|
|
124
|
-
return f'{{"error": "{e}", "results": []}}'
|
|
125
|
-
|
|
126
|
-
@mcp.tool()
|
|
127
|
-
async def reflect(query: str, context: str | None = None, budget: str = "low", bank_id: str | None = None) -> str:
|
|
128
|
-
"""
|
|
129
|
-
Generate thoughtful analysis by synthesizing stored memories with the bank's personality.
|
|
130
|
-
|
|
131
|
-
WHEN TO USE THIS TOOL:
|
|
132
|
-
Use reflect when you need reasoned analysis, not just fact retrieval. This tool
|
|
133
|
-
thinks through the question using everything the bank knows and its personality traits.
|
|
134
|
-
|
|
135
|
-
EXAMPLES OF GOOD QUERIES:
|
|
136
|
-
- "What patterns have emerged in how I approach debugging?"
|
|
137
|
-
- "Based on my past decisions, what architectural style do I prefer?"
|
|
138
|
-
- "What might be the best approach for this problem given what you know about me?"
|
|
139
|
-
- "How should I prioritize these tasks based on my goals?"
|
|
140
|
-
|
|
141
|
-
HOW IT DIFFERS FROM RECALL:
|
|
142
|
-
- recall: Returns raw facts matching your search (fast lookup)
|
|
143
|
-
- reflect: Reasons across memories to form a synthesized answer (deeper analysis)
|
|
144
|
-
|
|
145
|
-
Use recall for "what did I say about X?" and reflect for "what should I do about X?"
|
|
146
|
-
|
|
147
|
-
Args:
|
|
148
|
-
query: The question or topic to reflect on
|
|
149
|
-
context: Optional context about why this reflection is needed
|
|
150
|
-
budget: Search budget - 'low', 'mid', or 'high' (default: 'low')
|
|
151
|
-
bank_id: Optional bank to reflect in (defaults to session bank). Use for cross-bank operations.
|
|
152
|
-
"""
|
|
153
|
-
try:
|
|
154
|
-
target_bank = bank_id or get_current_bank_id()
|
|
155
|
-
if target_bank is None:
|
|
156
|
-
return "Error: No bank_id configured"
|
|
157
|
-
from hindsight_api.engine.memory_engine import Budget
|
|
158
|
-
|
|
159
|
-
# Map string budget to enum
|
|
160
|
-
budget_map = {"low": Budget.LOW, "mid": Budget.MID, "high": Budget.HIGH}
|
|
161
|
-
budget_enum = budget_map.get(budget.lower(), Budget.LOW)
|
|
162
|
-
|
|
163
|
-
reflect_result = await memory.reflect_async(
|
|
164
|
-
bank_id=target_bank,
|
|
165
|
-
query=query,
|
|
166
|
-
budget=budget_enum,
|
|
167
|
-
context=context,
|
|
168
|
-
request_context=RequestContext(),
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
return reflect_result.model_dump_json(indent=2)
|
|
172
|
-
except Exception as e:
|
|
173
|
-
logger.error(f"Error reflecting: {e}", exc_info=True)
|
|
174
|
-
return f'{{"error": "{e}", "text": ""}}'
|
|
175
|
-
|
|
176
|
-
@mcp.tool()
|
|
177
|
-
async def list_banks() -> str:
|
|
178
|
-
"""
|
|
179
|
-
List all available memory banks.
|
|
180
|
-
|
|
181
|
-
Use this to discover banks for orchestration or to find
|
|
182
|
-
the correct bank_id for cross-bank operations.
|
|
183
|
-
|
|
184
|
-
Returns:
|
|
185
|
-
JSON object with banks array containing bank_id, name, disposition, background, and timestamps
|
|
186
|
-
"""
|
|
187
|
-
try:
|
|
188
|
-
banks = await memory.list_banks(request_context=RequestContext())
|
|
189
|
-
bank_items = [
|
|
190
|
-
BankListItem(
|
|
191
|
-
bank_id=b.get("bank_id") or b.get("id"),
|
|
192
|
-
name=b.get("name"),
|
|
193
|
-
disposition=DispositionTraits(
|
|
194
|
-
**b.get("disposition", {"skepticism": 3, "literalism": 3, "empathy": 3})
|
|
195
|
-
),
|
|
196
|
-
background=b.get("background"),
|
|
197
|
-
created_at=str(b.get("created_at")) if b.get("created_at") else None,
|
|
198
|
-
updated_at=str(b.get("updated_at")) if b.get("updated_at") else None,
|
|
199
|
-
)
|
|
200
|
-
for b in banks
|
|
201
|
-
]
|
|
202
|
-
return BankListResponse(banks=bank_items).model_dump_json(indent=2)
|
|
203
|
-
except Exception as e:
|
|
204
|
-
logger.error(f"Error listing banks: {e}", exc_info=True)
|
|
205
|
-
return f'{{"error": "{e}", "banks": []}}'
|
|
206
|
-
|
|
207
|
-
@mcp.tool()
|
|
208
|
-
async def create_bank(bank_id: str, name: str | None = None, background: str | None = None) -> str:
|
|
209
|
-
"""
|
|
210
|
-
Create or update a memory bank.
|
|
211
|
-
|
|
212
|
-
Use this to create new banks for different agents, sessions, or purposes.
|
|
213
|
-
Banks are isolated memory stores - each bank has its own memories and personality.
|
|
214
|
-
|
|
215
|
-
Args:
|
|
216
|
-
bank_id: Unique identifier for the bank (e.g., 'orchestrator-memory', 'agent-1')
|
|
217
|
-
name: Human-readable name for the bank
|
|
218
|
-
background: Context about what this bank stores or its purpose
|
|
219
|
-
"""
|
|
220
|
-
try:
|
|
221
|
-
# Get or create the bank profile (auto-creates with defaults)
|
|
222
|
-
await memory.get_bank_profile(bank_id, request_context=RequestContext())
|
|
223
|
-
|
|
224
|
-
# Update name and/or background if provided
|
|
225
|
-
if name is not None or background is not None:
|
|
226
|
-
await memory.update_bank(bank_id, name=name, background=background, request_context=RequestContext())
|
|
227
|
-
|
|
228
|
-
# Get final profile and return using BankProfileResponse model
|
|
229
|
-
profile = await memory.get_bank_profile(bank_id, request_context=RequestContext())
|
|
230
|
-
disposition = profile.get("disposition")
|
|
231
|
-
if hasattr(disposition, "model_dump"):
|
|
232
|
-
disposition_traits = DispositionTraits(**disposition.model_dump())
|
|
233
|
-
else:
|
|
234
|
-
disposition_traits = DispositionTraits(
|
|
235
|
-
**dict(disposition or {"skepticism": 3, "literalism": 3, "empathy": 3})
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
response = BankProfileResponse(
|
|
239
|
-
bank_id=bank_id,
|
|
240
|
-
name=profile.get("name") or "",
|
|
241
|
-
disposition=disposition_traits,
|
|
242
|
-
background=profile.get("background") or "",
|
|
243
|
-
)
|
|
244
|
-
return response.model_dump_json(indent=2)
|
|
245
|
-
except Exception as e:
|
|
246
|
-
logger.error(f"Error creating bank: {e}", exc_info=True)
|
|
247
|
-
return json.dumps({"error": str(e)})
|
|
54
|
+
# Configure and register tools using shared module
|
|
55
|
+
config = MCPToolsConfig(
|
|
56
|
+
bank_id_resolver=get_current_bank_id,
|
|
57
|
+
include_bank_id_param=True, # HTTP MCP supports multi-bank via parameter
|
|
58
|
+
tools=None, # All tools
|
|
59
|
+
retain_fire_and_forget=False, # HTTP MCP supports sync/async modes
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
register_mcp_tools(mcp, memory, config)
|
|
248
63
|
|
|
249
64
|
return mcp
|
|
250
65
|
|