Skip to content

Commit 6d7e96e

Browse files
authored
Merge pull request #1 from SameThoughts/init
initial SDK commit
2 parents 7507935 + 3063541 commit 6d7e96e

28 files changed

Lines changed: 3637 additions & 1 deletion

README.md

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,169 @@
1-
# hippodid-python
1+
# hippodid
2+
3+
Python SDK for [HippoDid](https://hippodid.com) -- character memory for AI agents.
4+
5+
HippoDid gives your AI agents persistent identity: personality, background, rules, structured memories, and agent configuration. This SDK wraps the REST API and adds client-side context assembly for any LLM framework.
6+
7+
```bash
8+
pip install hippodid
9+
```
10+
11+
## Quick Start
12+
13+
```python
14+
from hippodid import HippoDid
15+
16+
hd = HippoDid(api_key="hd_your_key")
17+
18+
# Create a character
19+
char = hd.create_character(name="Ada", description="Senior engineer")
20+
21+
# Set up her profile
22+
hd.update_profile(
23+
char.id,
24+
system_prompt="You are Ada, a senior software engineer.",
25+
personality="Analytical, thorough, loves clean architecture",
26+
rules=["Always suggest tests", "Prefer functional patterns"],
27+
)
28+
29+
# Add memories
30+
hd.add_memory(char.id, "Ada led the migration from REST to GraphQL in Q3")
31+
hd.add_memory(char.id, "Ada prefers Rust for systems work, Python for scripting")
32+
33+
# Search memories
34+
results = hd.search_memories(char.id, "programming languages")
35+
```
36+
37+
## Context Assembly
38+
39+
The killer feature: `assemble_context` builds a complete LLM prompt from character profile + memories in one call.
40+
41+
```python
42+
import anthropic
43+
from hippodid import HippoDid
44+
45+
hd = HippoDid(api_key="hd_your_key")
46+
claude = anthropic.Anthropic()
47+
48+
# One call: fetch profile + search memories + format prompt
49+
context = hd.assemble_context(char_id, "What should we refactor?", strategy="task_focused")
50+
51+
response = claude.messages.create(
52+
model="claude-sonnet-4-20250514",
53+
max_tokens=1024,
54+
system=context.formatted_prompt,
55+
messages=[{"role": "user", "content": "What should we refactor?"}],
56+
)
57+
58+
# Save the exchange
59+
hd.add_memory(char_id, f"Discussed refactoring: {response.content[0].text}")
60+
```
61+
62+
### Assembly Strategies
63+
64+
Same character data, different prompt formatting:
65+
66+
| Strategy | Best For | Emphasis |
67+
|---|---|---|
68+
| `default` | General use | system_prompt + profile + memories by relevance |
69+
| `conversational` | Chat agents | Personality/tone, recent episodic memories |
70+
| `task_focused` | Work agents | Rules/constraints, project context, decisions |
71+
| `concierge` | Service agents | Preferences/history, proactive suggestions |
72+
| `matching` | Cross-character | Profile-heavy, minimal memories |
73+
74+
## Async Support
75+
76+
```python
77+
from hippodid import AsyncHippoDid
78+
79+
async with AsyncHippoDid(api_key="hd_your_key") as hd:
80+
char = await hd.create_character(name="Ada")
81+
await hd.add_memory(char.id, "Ada loves async Python")
82+
context = await hd.assemble_context(char.id, "async patterns")
83+
```
84+
85+
## Character Templates & Batch Create
86+
87+
```python
88+
# Create a template for batch character creation
89+
template = hd.create_character_template(
90+
name="Sales Rep",
91+
field_mappings=[
92+
{"sourceColumn": "name", "targetField": "name"},
93+
{"sourceColumn": "crm_id", "targetField": "externalId"},
94+
],
95+
)
96+
97+
# Batch create from a list of dicts (also accepts pandas DataFrames or file paths)
98+
job = hd.batch_create_characters(
99+
template_id=template.id,
100+
data=[
101+
{"name": "Alice", "crm_id": "SF-001"},
102+
{"name": "Bob", "crm_id": "SF-002"},
103+
],
104+
external_id_column="crm_id",
105+
)
106+
107+
# Poll for completion
108+
status = hd.get_batch_job_status(job.job_id)
109+
```
110+
111+
## Agent Config
112+
113+
Store LLM preferences per character:
114+
115+
```python
116+
hd.set_agent_config(
117+
char_id,
118+
system_prompt="You are Ada, a senior engineer.",
119+
preferred_model="claude-sonnet-4-20250514",
120+
temperature=0.3,
121+
tools=["code_search", "run_tests"],
122+
)
123+
124+
# RAG: ask a question using character's memories + stored agent config
125+
result = hd.ask(char_id, "What patterns should we use?", use_agent_config=True)
126+
print(result.answer)
127+
```
128+
129+
## Memory Modes
130+
131+
Control how `add_memory` processes content:
132+
133+
```python
134+
hd.set_memory_mode(char_id, "VERBATIM") # Store exact content, zero LLM cost
135+
hd.set_memory_mode(char_id, "EXTRACTED") # AI extracts structured facts (default)
136+
hd.set_memory_mode(char_id, "HYBRID") # Both extraction + verbatim (Business+)
137+
```
138+
139+
## Clone Characters
140+
141+
```python
142+
clone = hd.clone_character(
143+
char_id,
144+
"Ada-Staging",
145+
copy_memories=True,
146+
copy_tags=True,
147+
)
148+
print(f"Cloned: {clone.character.id}, {clone.memories_copied} memories copied")
149+
```
150+
151+
## Framework Examples
152+
153+
See the `examples/` directory:
154+
155+
- **[claude_hippodid_direct.py](examples/claude_hippodid_direct.py)** -- 10-line Claude integration
156+
- **[langchain_hippodid_memory.py](examples/langchain_hippodid_memory.py)** -- Drop-in BaseChatMemory subclass
157+
- **[crewai_hippodid_memory.py](examples/crewai_hippodid_memory.py)** -- CrewAI agents with HippoDid identity
158+
- **[openai_agents_hippodid.py](examples/openai_agents_hippodid.py)** -- OpenAI Agents SDK with memory tools
159+
- **[luxury_concierge.py](examples/luxury_concierge.py)** -- Assembly strategy showcase
160+
- **[salesforce_to_hippodid.py](examples/salesforce_to_hippodid.py)** -- End-to-end batch pipeline
161+
- **[healer_matching.py](examples/healer_matching.py)** -- Cross-character matching
162+
163+
## API Reference
164+
165+
Full documentation at [docs.hippodid.com](https://docs.hippodid.com).
166+
167+
## License
168+
169+
MIT

examples/claude_hippodid_direct.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Simplest Claude + HippoDid integration (10 lines).
3+
4+
Shows: assemble_context -> Claude messages API -> save exchange as memory.
5+
6+
Requirements: pip install hippodid anthropic
7+
"""
8+
9+
import anthropic
10+
from hippodid import HippoDid
11+
12+
hd = HippoDid(api_key="hd_your_key")
13+
claude = anthropic.Anthropic()
14+
15+
CHAR_ID = "your-character-uuid" # replace with your character ID
16+
USER_MSG = "What projects am I working on?"
17+
18+
# 1. Assemble context from character profile + memories
19+
context = hd.assemble_context(CHAR_ID, USER_MSG, strategy="conversational")
20+
21+
# 2. Call Claude with the assembled prompt
22+
response = claude.messages.create(
23+
model=context.config.preferred_model if context.config else "claude-sonnet-4-20250514",
24+
max_tokens=1024,
25+
system=context.formatted_prompt,
26+
messages=[{"role": "user", "content": USER_MSG}],
27+
)
28+
29+
answer = response.content[0].text
30+
print(answer)
31+
32+
# 3. Save the exchange as a new memory
33+
hd.add_memory(CHAR_ID, f"User asked: {USER_MSG}\nAssistant answered: {answer}")

examples/crewai_hippodid_memory.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
CrewAI + HippoDid: Agents with persistent character identity.
3+
4+
Each agent loads its character identity at task start using
5+
strategy="task_focused" for work agents.
6+
7+
Requirements: pip install hippodid crewai
8+
"""
9+
10+
from crewai import Agent, Task, Crew
11+
12+
from hippodid import HippoDid
13+
14+
15+
def make_hippodid_agent(
16+
hd: HippoDid,
17+
character_id: str,
18+
role: str,
19+
goal: str,
20+
task_query: str,
21+
) -> Agent:
22+
"""Create a CrewAI agent with HippoDid character identity."""
23+
context = hd.assemble_context(
24+
character_id,
25+
task_query,
26+
strategy="task_focused",
27+
max_context_tokens=3000,
28+
)
29+
30+
return Agent(
31+
role=role,
32+
goal=goal,
33+
backstory=context.formatted_prompt,
34+
verbose=True,
35+
)
36+
37+
38+
if __name__ == "__main__":
39+
hd = HippoDid(api_key="hd_your_key")
40+
41+
# Create agents with HippoDid-backed identities
42+
researcher = make_hippodid_agent(
43+
hd,
44+
character_id="researcher-char-uuid",
45+
role="Senior Researcher",
46+
goal="Find and synthesize information",
47+
task_query="research and analysis tasks",
48+
)
49+
50+
writer = make_hippodid_agent(
51+
hd,
52+
character_id="writer-char-uuid",
53+
role="Technical Writer",
54+
goal="Write clear, concise documentation",
55+
task_query="writing and documentation tasks",
56+
)
57+
58+
# Define tasks
59+
research_task = Task(
60+
description="Research the latest trends in AI agent frameworks",
61+
expected_output="A summary of top 5 AI agent frameworks with pros and cons",
62+
agent=researcher,
63+
)
64+
65+
writing_task = Task(
66+
description="Write a blog post based on the research findings",
67+
expected_output="A 500-word blog post about AI agent frameworks",
68+
agent=writer,
69+
)
70+
71+
# Run the crew
72+
crew = Crew(agents=[researcher, writer], tasks=[research_task, writing_task], verbose=True)
73+
result = crew.kickoff()
74+
print(result)
75+
76+
# Save task outcomes back as memories
77+
hd.add_memory("researcher-char-uuid", f"Completed research task: {research_task.description}")
78+
hd.add_memory("writer-char-uuid", f"Completed writing task: {writing_task.description}")

examples/healer_matching.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Cross-character search for healer matching.
3+
4+
NOTE: Requires Sprint 19 cross-character search API (not yet available).
5+
This example shows the intended usage pattern.
6+
7+
Requirements: pip install hippodid
8+
"""
9+
10+
from hippodid import HippoDid
11+
12+
hd = HippoDid(api_key="hd_your_key")
13+
14+
15+
def match_healer(patient_query: str) -> None:
16+
"""Find the best healer match for a patient's needs.
17+
18+
Uses the 'matching' assembly strategy which is profile-heavy
19+
with minimal memories -- optimized for cross-character comparison.
20+
21+
NOTE: This example requires Sprint 19's cross-character search API.
22+
Currently, you would need to iterate over healers manually.
23+
"""
24+
# Sprint 19 will add: hd.search_characters(query=patient_query, tag="healer")
25+
# For now, list healers by tag and compare manually
26+
healers = hd.list_characters(tag="healer", limit=50)
27+
28+
matches = []
29+
for healer in healers:
30+
ctx = hd.assemble_context(
31+
healer.id,
32+
patient_query,
33+
strategy="matching",
34+
max_context_tokens=1000,
35+
)
36+
matches.append({
37+
"character": healer,
38+
"context": ctx,
39+
"token_estimate": ctx.token_estimate,
40+
})
41+
42+
# Sort by context richness (more relevant profile = better match)
43+
matches.sort(key=lambda m: m["token_estimate"], reverse=True)
44+
45+
print(f"Top matches for: '{patient_query}'")
46+
for i, m in enumerate(matches[:5], 1):
47+
char = m["character"]
48+
print(f" {i}. {char.name} (memories: {char.memory_count})")
49+
print(f" Profile preview: {m['context'].profile[:100]}...")
50+
51+
52+
if __name__ == "__main__":
53+
match_healer("chronic back pain, prefers holistic approaches")

0 commit comments

Comments
 (0)