tidaldb/applications/iknowyou/lib/synap.ts
Alan Kahn 8b39409901 feat(iknowyou): add SSH tunnel script, Synap space header, and hydration fix
- Add tunnel.sh for SSH tunneling to vLLM server (port 8000 is
  firewalled, tunnel required for local dev)
- Add dev:tunnel script to package.json for running dev with tunnel
- Add SYNAP_SPACE env var support to synap.ts, sends X-Memory-Space-Id
  header when set
- Fix SSR hydration mismatch in person-switcher by deferring personId
  render until client mount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:18:34 -05:00

164 lines
3.7 KiB
TypeScript

const SYNAP_URL =
process.env.SYNAP_URL ?? "https://api.synap.orchard9.ai";
const SYNAP_API_KEY = process.env.SYNAP_API_KEY ?? "";
const SYNAP_SPACE = process.env.SYNAP_SPACE ?? "";
// --- Response types ---
export interface SynapConfidence {
value: number;
category: string;
}
export interface SynapMessageResponse {
message_id: string;
stored_at: string;
activated_memories?: SynapActivatedMemory[];
}
export interface SynapActivatedMemory {
id: string;
content: string;
confidence: SynapConfidence;
}
export interface SynapMessage {
id: string;
user_id: string;
content: string;
timestamp: string;
}
export interface SynapMessagesResponse {
messages: SynapMessage[];
activated_memories?: SynapActivatedMemory[];
pagination: { offset: number; limit: number; returned: number };
}
export interface SynapRecallMemory {
id: string;
content: string;
confidence: SynapConfidence;
activation_level: number;
}
export interface SynapRecallResponse {
memories: {
vivid: SynapRecallMemory[];
associated: SynapRecallMemory[];
reconstructed: SynapRecallMemory[];
};
recall_confidence: SynapConfidence;
}
export interface SynapRememberResponse {
memory_id: string;
observed_at: string;
stored_at: string;
storage_confidence: SynapConfidence;
}
// --- Client ---
async function request<T>(
path: string,
init?: RequestInit
): Promise<T> {
const res = await fetch(`${SYNAP_URL}${path}`, {
...init,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${SYNAP_API_KEY}`,
...(SYNAP_SPACE ? { "X-Memory-Space-Id": SYNAP_SPACE } : {}),
...init?.headers,
},
});
if (!res.ok) {
const body = await res.text().catch(() => "");
throw new Error(`Synap ${res.status}: ${body}`);
}
return res.json();
}
/** Store a message in a conversation. */
export function sendMessage(
userId: string,
message: string,
conversationId: string
): Promise<SynapMessageResponse> {
return request("/api/v1/messages", {
method: "POST",
body: JSON.stringify({
user_id: userId,
message,
conversation_id: conversationId,
}),
});
}
/** Retrieve messages for a conversation. */
export function getMessages(
conversationId: string,
limit = 50,
offset = 0
): Promise<SynapMessagesResponse> {
const params = new URLSearchParams({
conversation_id: conversationId,
limit: String(limit),
offset: String(offset),
});
return request(`/api/v1/messages?${params}`);
}
/** Recall relevant memories by natural language query. */
export function recall(
query: string,
maxResults = 5,
threshold = 0.5
): Promise<SynapRecallResponse> {
const params = new URLSearchParams({
query,
max_results: String(maxResults),
threshold: String(threshold),
});
return request(`/api/v1/memories/recall?${params}`);
}
/** Recall memories filtered by tags. */
export function recallByTag(
query: string,
tags: string[],
maxResults = 10,
threshold = 0.3
): Promise<SynapRecallResponse> {
const params = new URLSearchParams({
query,
max_results: String(maxResults),
threshold: String(threshold),
tags: tags.join(","),
});
return request(`/api/v1/memories/recall?${params}`);
}
/** Store a new memory. */
export function remember(
content: string,
opts: {
confidence?: number;
memoryType?: "semantic" | "episodic" | "procedural";
tags?: string[];
} = {}
): Promise<SynapRememberResponse> {
return request("/api/v1/memories/remember", {
method: "POST",
body: JSON.stringify({
content,
confidence: opts.confidence ?? 0.7,
memory_type: opts.memoryType ?? "semantic",
tags: opts.tags,
}),
});
}