409 lines
12 KiB
Python
409 lines
12 KiB
Python
"""
|
|
Clean, pythonic wrapper for the Hindsight API client.
|
|
|
|
This file is MAINTAINED and NOT auto-generated. It provides a high-level,
|
|
easy-to-use interface on top of the auto-generated OpenAPI client.
|
|
"""
|
|
|
|
import asyncio
|
|
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
|
|
import hindsight_client_api
|
|
from hindsight_client_api.api import memory_operations_api, reasoning_api, agent_management_api
|
|
from hindsight_client_api.models import (
|
|
search_request,
|
|
batch_put_request,
|
|
memory_item,
|
|
think_request,
|
|
)
|
|
|
|
|
|
def _run_async(coro):
|
|
"""Run an async coroutine synchronously."""
|
|
try:
|
|
loop = asyncio.get_event_loop()
|
|
except RuntimeError:
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
|
|
return loop.run_until_complete(coro)
|
|
|
|
|
|
class Hindsight:
|
|
"""
|
|
High-level, easy-to-use Hindsight API client.
|
|
|
|
Example:
|
|
```python
|
|
from hindsight_client import Hindsight
|
|
|
|
client = Hindsight(base_url="http://localhost:8888")
|
|
|
|
# Store a memory
|
|
client.put(agent_id="alice", content="Alice loves AI")
|
|
|
|
# Search memories
|
|
results = client.search(agent_id="alice", query="What does Alice like?")
|
|
|
|
# Generate contextual answer
|
|
answer = client.think(agent_id="alice", query="What are my interests?")
|
|
```
|
|
"""
|
|
|
|
def __init__(self, base_url: str, timeout: float = 30.0):
|
|
"""
|
|
Initialize the Hindsight client.
|
|
|
|
Args:
|
|
base_url: The base URL of the Hindsight API server
|
|
timeout: Request timeout in seconds (default: 30.0)
|
|
"""
|
|
config = hindsight_client_api.Configuration(host=base_url)
|
|
self._api_client = hindsight_client_api.ApiClient(config)
|
|
self._memory_api = memory_operations_api.MemoryOperationsApi(self._api_client)
|
|
self._reasoning_api = reasoning_api.ReasoningApi(self._api_client)
|
|
self._agent_api = agent_management_api.AgentManagementApi(self._api_client)
|
|
|
|
def __enter__(self):
|
|
"""Context manager entry."""
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""Context manager exit."""
|
|
self.close()
|
|
|
|
def close(self):
|
|
"""Close the API client."""
|
|
if self._api_client:
|
|
_run_async(self._api_client.close())
|
|
|
|
# Simplified methods for main operations
|
|
|
|
def put(
|
|
self,
|
|
agent_id: str,
|
|
content: str,
|
|
event_date: Optional[datetime] = None,
|
|
context: Optional[str] = None,
|
|
document_id: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Store a single memory (simplified interface).
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
content: Memory content
|
|
event_date: Optional event timestamp
|
|
context: Optional context description
|
|
document_id: Optional document ID for grouping
|
|
|
|
Returns:
|
|
Response with success status
|
|
"""
|
|
return self.put_batch(
|
|
agent_id=agent_id,
|
|
items=[{"content": content, "event_date": event_date, "context": context}],
|
|
document_id=document_id,
|
|
)
|
|
|
|
def put_batch(
|
|
self,
|
|
agent_id: str,
|
|
items: List[Dict[str, Any]],
|
|
document_id: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Store multiple memories in batch.
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
items: List of memory items with 'content' and optional 'event_date', 'context'
|
|
document_id: Optional document ID for grouping memories
|
|
|
|
Returns:
|
|
Response with success status and item count
|
|
"""
|
|
memory_items = [
|
|
memory_item.MemoryItem(
|
|
content=item["content"],
|
|
event_date=item.get("event_date"),
|
|
context=item.get("context"),
|
|
)
|
|
for item in items
|
|
]
|
|
|
|
request_obj = batch_put_request.BatchPutRequest(
|
|
items=memory_items,
|
|
document_id=document_id,
|
|
)
|
|
|
|
response = _run_async(self._memory_api.batch_put_memories(agent_id, request_obj))
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
def search(
|
|
self,
|
|
agent_id: str,
|
|
query: str,
|
|
fact_type: Optional[List[str]] = None,
|
|
max_tokens: int = 4096,
|
|
thinking_budget: int = 100,
|
|
) -> List[Dict[str, Any]]:
|
|
"""
|
|
Search memories using semantic similarity.
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
query: Search query
|
|
fact_type: Optional list of fact types to filter (world, agent, opinion)
|
|
max_tokens: Maximum tokens in results (default: 4096)
|
|
thinking_budget: Token budget for search (default: 100)
|
|
|
|
Returns:
|
|
List of search results
|
|
"""
|
|
request_obj = search_request.SearchRequest(
|
|
query=query,
|
|
fact_type=fact_type,
|
|
thinking_budget=thinking_budget,
|
|
max_tokens=max_tokens,
|
|
trace=False,
|
|
)
|
|
|
|
response = _run_async(self._memory_api.search_memories(agent_id, request_obj))
|
|
|
|
if hasattr(response, 'results'):
|
|
return [r.to_dict() if hasattr(r, 'to_dict') else r for r in response.results]
|
|
return []
|
|
|
|
def think(
|
|
self,
|
|
agent_id: str,
|
|
query: str,
|
|
thinking_budget: int = 50,
|
|
context: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Generate a contextual answer based on agent identity and memories.
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
query: The question or prompt
|
|
thinking_budget: Token budget for thinking (default: 50)
|
|
context: Optional additional context
|
|
|
|
Returns:
|
|
Response with answer text, facts used, and new opinions
|
|
"""
|
|
request_obj = think_request.ThinkRequest(
|
|
query=query,
|
|
thinking_budget=thinking_budget,
|
|
context=context,
|
|
)
|
|
|
|
response = _run_async(self._reasoning_api.think(agent_id, request_obj))
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
# Full-featured methods (expose more options)
|
|
|
|
def search_memories(
|
|
self,
|
|
agent_id: str,
|
|
query: str,
|
|
fact_type: Optional[List[str]] = None,
|
|
thinking_budget: int = 100,
|
|
max_tokens: int = 4096,
|
|
trace: bool = False,
|
|
question_date: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Search memories with all options (full-featured).
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
query: Search query
|
|
fact_type: Optional list of fact types to filter
|
|
thinking_budget: Token budget for thinking
|
|
max_tokens: Maximum tokens in results
|
|
trace: Enable trace output
|
|
question_date: Optional ISO format date string
|
|
|
|
Returns:
|
|
Full search response with results and optional trace
|
|
"""
|
|
request_obj = search_request.SearchRequest(
|
|
query=query,
|
|
fact_type=fact_type,
|
|
thinking_budget=thinking_budget,
|
|
max_tokens=max_tokens,
|
|
trace=trace,
|
|
question_date=question_date,
|
|
)
|
|
|
|
response = _run_async(self._memory_api.search_memories(agent_id, request_obj))
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
def list_memories(
|
|
self,
|
|
agent_id: str,
|
|
fact_type: Optional[str] = None,
|
|
search_query: Optional[str] = None,
|
|
limit: int = 100,
|
|
offset: int = 0,
|
|
) -> Dict[str, Any]:
|
|
"""List memory units with pagination."""
|
|
response = _run_async(self._memory_api.list_memories(
|
|
agent_id=agent_id,
|
|
fact_type=fact_type,
|
|
q=search_query,
|
|
limit=limit,
|
|
offset=offset,
|
|
))
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
def create_agent(
|
|
self,
|
|
agent_id: str,
|
|
name: Optional[str] = None,
|
|
background: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""Create or update an agent."""
|
|
from hindsight_client_api.models import create_agent_request
|
|
|
|
request_obj = create_agent_request.CreateAgentRequest(
|
|
name=name,
|
|
background=background,
|
|
)
|
|
|
|
response = _run_async(self._agent_api.create_or_update_agent(agent_id, request_obj))
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
# Async methods (native async, no _run_async wrapper)
|
|
|
|
async def aput_batch(
|
|
self,
|
|
agent_id: str,
|
|
items: List[Dict[str, Any]],
|
|
document_id: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Store multiple memories in batch (async).
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
items: List of memory items with 'content' and optional 'event_date', 'context'
|
|
document_id: Optional document ID for grouping memories
|
|
|
|
Returns:
|
|
Response with success status and item count
|
|
"""
|
|
memory_items = [
|
|
memory_item.MemoryItem(
|
|
content=item["content"],
|
|
event_date=item.get("event_date"),
|
|
context=item.get("context"),
|
|
)
|
|
for item in items
|
|
]
|
|
|
|
request_obj = batch_put_request.BatchPutRequest(
|
|
items=memory_items,
|
|
document_id=document_id,
|
|
)
|
|
|
|
response = await self._memory_api.batch_put_memories(agent_id, request_obj)
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
async def aput(
|
|
self,
|
|
agent_id: str,
|
|
content: str,
|
|
event_date: Optional[datetime] = None,
|
|
context: Optional[str] = None,
|
|
document_id: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Store a single memory (async).
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
content: Memory content
|
|
event_date: Optional event timestamp
|
|
context: Optional context description
|
|
document_id: Optional document ID for grouping
|
|
|
|
Returns:
|
|
Response with success status
|
|
"""
|
|
return await self.aput_batch(
|
|
agent_id=agent_id,
|
|
items=[{"content": content, "event_date": event_date, "context": context}],
|
|
document_id=document_id,
|
|
)
|
|
|
|
async def asearch(
|
|
self,
|
|
agent_id: str,
|
|
query: str,
|
|
fact_type: Optional[List[str]] = None,
|
|
max_tokens: int = 4096,
|
|
thinking_budget: int = 100,
|
|
) -> List[Dict[str, Any]]:
|
|
"""
|
|
Search memories using semantic similarity (async).
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
query: Search query
|
|
fact_type: Optional list of fact types to filter (world, agent, opinion)
|
|
max_tokens: Maximum tokens in results (default: 4096)
|
|
thinking_budget: Token budget for search (default: 100)
|
|
|
|
Returns:
|
|
List of search results
|
|
"""
|
|
request_obj = search_request.SearchRequest(
|
|
query=query,
|
|
fact_type=fact_type,
|
|
thinking_budget=thinking_budget,
|
|
max_tokens=max_tokens,
|
|
trace=False,
|
|
)
|
|
|
|
response = await self._memory_api.search_memories(agent_id, request_obj)
|
|
|
|
if hasattr(response, 'results'):
|
|
return [r.to_dict() if hasattr(r, 'to_dict') else r for r in response.results]
|
|
return []
|
|
|
|
async def athink(
|
|
self,
|
|
agent_id: str,
|
|
query: str,
|
|
thinking_budget: int = 50,
|
|
context: Optional[str] = None,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Generate a contextual answer based on agent identity and memories (async).
|
|
|
|
Args:
|
|
agent_id: The agent ID
|
|
query: The question or prompt
|
|
thinking_budget: Token budget for thinking (default: 50)
|
|
context: Optional additional context
|
|
|
|
Returns:
|
|
Response with answer text, facts used, and new opinions
|
|
"""
|
|
request_obj = think_request.ThinkRequest(
|
|
query=query,
|
|
thinking_budget=thinking_budget,
|
|
context=context,
|
|
)
|
|
|
|
response = await self._reasoning_api.think(agent_id, request_obj)
|
|
return response.to_dict() if hasattr(response, 'to_dict') else response
|
|
|
|
|
|
# Alias for backward compatibility
|
|
HindsightClient = Hindsight
|