pipecat-memorysync 1.1.1__tar.gz → 1.2.0__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.
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/.gitignore +5 -5
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/CHANGELOG.md +51 -51
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/PKG-INFO +28 -23
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/README.md +151 -146
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/examples/foundational.py +130 -128
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/pyproject.toml +43 -43
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/src/pipecat_memorysync/__init__.py +32 -31
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/src/pipecat_memorysync/_api.py +302 -287
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/src/pipecat_memorysync/_version.py +3 -3
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/src/pipecat_memorysync/service.py +363 -360
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/tests/conftest.py +265 -171
- {pipecat_memorysync-1.1.1 → pipecat_memorysync-1.2.0}/tests/test_service.py +368 -310
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
venv/
|
|
2
|
-
__pycache__/
|
|
3
|
-
.pytest_cache/
|
|
4
|
-
dist/
|
|
5
|
-
*.egg-info/
|
|
1
|
+
venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
.pytest_cache/
|
|
4
|
+
dist/
|
|
5
|
+
*.egg-info/
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `pipecat-memorysync` are documented here.
|
|
4
|
-
|
|
5
|
-
## 1.1.1 — 2026-08-29
|
|
6
|
-
|
|
7
|
-
- **Immediate, loss-proof capture.** 1.1.0's turn-complete merging
|
|
8
|
-
deferred the current utterance until the turn completed — on a browser
|
|
9
|
-
disconnect that deferral raced the brief cancel salvage window and
|
|
10
|
-
could lose the newest (usually most important) turn over slow
|
|
11
|
-
networks. Capture is immediate again: every new user/assistant message
|
|
12
|
-
is in flight the moment its frame passes, BEFORE the LLM replies.
|
|
13
|
-
Junk filtering and fact extraction now happen server-side (the
|
|
14
|
-
platform's low-value gate + conversational ingestion), so client-side
|
|
15
|
-
merging is unnecessary. Suite: 17 checks on Pipecat v1.8.1, including
|
|
16
|
-
the exact disconnect sequence that previously lost data.
|
|
17
|
-
|
|
18
|
-
## 1.1.0 — 2026-08-29
|
|
19
|
-
|
|
20
|
-
- **Turn-complete capture.** Voice aggregators split one utterance across
|
|
21
|
-
several context messages at speech pauses; those fragments previously
|
|
22
|
-
stored as separate rows ("and", "dinner", …). Consecutive new user
|
|
23
|
-
fragments now merge into ONE verbatim turn, stored when the assistant
|
|
24
|
-
reply completes the turn; an in-progress utterance is flushed as one
|
|
25
|
-
merged turn at end of call. Assistant turns still store immediately,
|
|
26
|
-
and a failed store releases its fragments for retry on the next frame.
|
|
27
|
-
- Delta-only and idempotency guarantees unchanged; suite grows to 17
|
|
28
|
-
checks on Pipecat v1.8.1 (fragment merging, end-of-call tail flush,
|
|
29
|
-
failed-store retry).
|
|
30
|
-
|
|
31
|
-
## 1.0.1 — 2026-08-29
|
|
32
|
-
|
|
33
|
-
- Metadata only: the source repository moved to
|
|
34
|
-
`https://github.com/memorysyncio/memorysync-pipecat`; project URLs updated.
|
|
35
|
-
No code changes.
|
|
36
|
-
|
|
37
|
-
## 1.0.0 — 2026-08-29
|
|
38
|
-
|
|
39
|
-
Initial release.
|
|
40
|
-
|
|
41
|
-
- `MemorySyncMemoryService`, a `FrameProcessor` for the position between the
|
|
42
|
-
user context aggregator and the LLM service.
|
|
43
|
-
- Budgeted memory recall (default 1.2 s hard timeout) injected into every
|
|
44
|
-
`LLMContextFrame` as a guarded system message — a slow or unreachable
|
|
45
|
-
backend degrades to an unenriched frame, never a stalled reply.
|
|
46
|
-
- Delta-only capture with deterministic idempotency seeds: only turns not
|
|
47
|
-
seen before are persisted, with role fidelity for user and assistant.
|
|
48
|
-
- Injection exclusion: the injected memory block is never re-captured.
|
|
49
|
-
- Graceful `EndFrame` flush (bounded 3 s window) and `CancelFrame` salvage.
|
|
50
|
-
- Tested with Pipecat v1.8.1 through `pipecat.tests.utils.run_test`
|
|
51
|
-
(14-test suite).
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `pipecat-memorysync` are documented here.
|
|
4
|
+
|
|
5
|
+
## 1.1.1 — 2026-08-29
|
|
6
|
+
|
|
7
|
+
- **Immediate, loss-proof capture.** 1.1.0's turn-complete merging
|
|
8
|
+
deferred the current utterance until the turn completed — on a browser
|
|
9
|
+
disconnect that deferral raced the brief cancel salvage window and
|
|
10
|
+
could lose the newest (usually most important) turn over slow
|
|
11
|
+
networks. Capture is immediate again: every new user/assistant message
|
|
12
|
+
is in flight the moment its frame passes, BEFORE the LLM replies.
|
|
13
|
+
Junk filtering and fact extraction now happen server-side (the
|
|
14
|
+
platform's low-value gate + conversational ingestion), so client-side
|
|
15
|
+
merging is unnecessary. Suite: 17 checks on Pipecat v1.8.1, including
|
|
16
|
+
the exact disconnect sequence that previously lost data.
|
|
17
|
+
|
|
18
|
+
## 1.1.0 — 2026-08-29
|
|
19
|
+
|
|
20
|
+
- **Turn-complete capture.** Voice aggregators split one utterance across
|
|
21
|
+
several context messages at speech pauses; those fragments previously
|
|
22
|
+
stored as separate rows ("and", "dinner", …). Consecutive new user
|
|
23
|
+
fragments now merge into ONE verbatim turn, stored when the assistant
|
|
24
|
+
reply completes the turn; an in-progress utterance is flushed as one
|
|
25
|
+
merged turn at end of call. Assistant turns still store immediately,
|
|
26
|
+
and a failed store releases its fragments for retry on the next frame.
|
|
27
|
+
- Delta-only and idempotency guarantees unchanged; suite grows to 17
|
|
28
|
+
checks on Pipecat v1.8.1 (fragment merging, end-of-call tail flush,
|
|
29
|
+
failed-store retry).
|
|
30
|
+
|
|
31
|
+
## 1.0.1 — 2026-08-29
|
|
32
|
+
|
|
33
|
+
- Metadata only: the source repository moved to
|
|
34
|
+
`https://github.com/memorysyncio/memorysync-pipecat`; project URLs updated.
|
|
35
|
+
No code changes.
|
|
36
|
+
|
|
37
|
+
## 1.0.0 — 2026-08-29
|
|
38
|
+
|
|
39
|
+
Initial release.
|
|
40
|
+
|
|
41
|
+
- `MemorySyncMemoryService`, a `FrameProcessor` for the position between the
|
|
42
|
+
user context aggregator and the LLM service.
|
|
43
|
+
- Budgeted memory recall (default 1.2 s hard timeout) injected into every
|
|
44
|
+
`LLMContextFrame` as a guarded system message — a slow or unreachable
|
|
45
|
+
backend degrades to an unenriched frame, never a stalled reply.
|
|
46
|
+
- Delta-only capture with deterministic idempotency seeds: only turns not
|
|
47
|
+
seen before are persisted, with role fidelity for user and assistant.
|
|
48
|
+
- Injection exclusion: the injected memory block is never re-captured.
|
|
49
|
+
- Graceful `EndFrame` flush (bounded 3 s window) and `CancelFrame` salvage.
|
|
50
|
+
- Tested with Pipecat v1.8.1 through `pipecat.tests.utils.run_test`
|
|
51
|
+
(14-test suite).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pipecat-memorysync
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: MemorySync for Pipecat: budgeted memory recall that never stalls a voice reply, delta-only
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: MemorySync for Pipecat: budgeted memory recall that never stalls a voice reply, delta-only fact capture from the caller's turns with idempotency seeds, and a pipeline that cannot be broken by a memory outage.
|
|
5
5
|
Project-URL: Homepage, https://docs.memorysync.io/guides/pipecat
|
|
6
6
|
Project-URL: Documentation, https://docs.memorysync.io/guides/pipecat
|
|
7
7
|
Project-URL: Repository, https://github.com/memorysyncio/memorysync-pipecat
|
|
@@ -26,7 +26,7 @@ Description-Content-Type: text/markdown
|
|
|
26
26
|
|
|
27
27
|
[MemorySync](https://memorysync.io) for [Pipecat](https://github.com/pipecat-ai/pipecat) —
|
|
28
28
|
long-term memory for voice pipelines that never stalls a reply and never
|
|
29
|
-
re-
|
|
29
|
+
re-sends a turn it already sent. PyPI package: **`pipecat-memorysync`**.
|
|
30
30
|
|
|
31
31
|
Built and maintained by the [MemorySync](https://memorysync.io) team —
|
|
32
32
|
MemorySync is our product, and this integration is actively maintained
|
|
@@ -73,28 +73,28 @@ pipeline = Pipeline([
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
Every `LLMContextFrame` that flows through is enriched with relevant memories
|
|
76
|
-
(as a system message) and
|
|
77
|
-
enriched or not, on time.
|
|
76
|
+
(as a system message) and its **new** user turns are sent to fact extraction —
|
|
77
|
+
then pushed on, enriched or not, on time.
|
|
78
78
|
|
|
79
79
|
## Design guarantees
|
|
80
80
|
|
|
81
81
|
- **Budgeted recall.** Enrichment runs under a hard timeout (default
|
|
82
82
|
**1.2 s**). A slow or dead memory backend means an unenriched frame, never a
|
|
83
83
|
stalled voice reply.
|
|
84
|
-
- **Immediate, loss-proof capture.** Every new message is sent the
|
|
85
|
-
its frame passes — the current utterance is in flight BEFORE the
|
|
86
|
-
replies, so a disconnect can never lose it.
|
|
87
|
-
extraction happen server-side
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
re-
|
|
93
|
-
- **Injection exclusion.** The memory block this service adds is never
|
|
94
|
-
back as a new
|
|
95
|
-
- **Graceful end, salvaged abort.** On `EndFrame`, queued
|
|
96
|
-
window (3 s) to land before the pipeline stops — the
|
|
97
|
-
not lost. On `CancelFrame`, the frame is pushed first and
|
|
84
|
+
- **Immediate, loss-proof capture.** Every new user message is sent the
|
|
85
|
+
moment its frame passes — the current utterance is in flight BEFORE the
|
|
86
|
+
LLM replies, so a disconnect can never lose it. Filler filtering and fact
|
|
87
|
+
extraction happen server-side: only the durable facts in what the caller
|
|
88
|
+
said are stored as memories, not the turn text.
|
|
89
|
+
- **Delta-only capture.** Only user messages *not seen before* are sent,
|
|
90
|
+
tracked by deterministic idempotency seeds; a retried turn is recognised
|
|
91
|
+
server-side and not extracted twice. Growing a 50-message context does not
|
|
92
|
+
re-send 50 messages per turn.
|
|
93
|
+
- **Injection exclusion.** The memory block this service adds is never sent
|
|
94
|
+
back as a new turn.
|
|
95
|
+
- **Graceful end, salvaged abort.** On `EndFrame`, queued sends get a bounded
|
|
96
|
+
window (3 s) to land before the pipeline stops — the caller's last words are
|
|
97
|
+
not lost. On `CancelFrame`, the frame is pushed first and sends get a brief
|
|
98
98
|
salvage window.
|
|
99
99
|
- **Failure-proof.** HTTP errors, quota limits, and timeouts all degrade to
|
|
100
100
|
"no memories this turn". Nothing propagates into the pipeline.
|
|
@@ -147,10 +147,15 @@ memory = MemorySyncMemoryService(
|
|
|
147
147
|
|
|
148
148
|
## Semantics worth knowing
|
|
149
149
|
|
|
150
|
-
-
|
|
151
|
-
|
|
150
|
+
- Only the caller's (**user**) turns are sent, as plain text with
|
|
151
|
+
`role: "user"`. The server extracts the durable facts they contain and
|
|
152
|
+
stores only those, tagged with `session_id: "pipecat::<session>"`.
|
|
153
|
+
Assistant replies are not sent — they are not stored as memories — and
|
|
154
|
+
filler such as "ok" or "thanks" stores nothing.
|
|
155
|
+
- Idempotency seeds let the server recognise retries and reconnects: the same
|
|
156
|
+
turn is not extracted twice.
|
|
152
157
|
- Free-tier quota exhaustion is silent by design (empty recall, accepted-but-
|
|
153
|
-
|
|
158
|
+
skipped sends); evaluation keys surface strict `429`s instead.
|
|
154
159
|
- The service is reusable across pipeline runs; call `await memory.aclose()`
|
|
155
160
|
from application shutdown if you want an explicit flush + client close.
|
|
156
161
|
|
|
@@ -158,7 +163,7 @@ memory = MemorySyncMemoryService(
|
|
|
158
163
|
|
|
159
164
|
```bash
|
|
160
165
|
python -m venv venv && venv/Scripts/pip install -e . pipecat-ai pytest pytest-asyncio
|
|
161
|
-
venv/Scripts/python -m pytest tests -q #
|
|
166
|
+
venv/Scripts/python -m pytest tests -q # 19 tests, run via pipecat's official test harness
|
|
162
167
|
```
|
|
163
168
|
|
|
164
169
|
## Docs
|
|
@@ -1,146 +1,151 @@
|
|
|
1
|
-
# memorysync-pipecat
|
|
2
|
-
|
|
3
|
-
[MemorySync](https://memorysync.io) for [Pipecat](https://github.com/pipecat-ai/pipecat) —
|
|
4
|
-
long-term memory for voice pipelines that never stalls a reply and never
|
|
5
|
-
re-
|
|
6
|
-
|
|
7
|
-
Built and maintained by the [MemorySync](https://memorysync.io) team —
|
|
8
|
-
MemorySync is our product, and this integration is actively maintained
|
|
9
|
-
alongside it.
|
|
10
|
-
|
|
11
|
-
**Tested with Pipecat v1.8.1** (`pipecat-ai>=1.0.0,<2`).
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
pip install pipecat-memorysync
|
|
15
|
-
# or
|
|
16
|
-
uv add pipecat-memorysync
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Where it sits
|
|
20
|
-
|
|
21
|
-
`MemorySyncMemoryService` is a `FrameProcessor`. Place it **between your
|
|
22
|
-
context aggregator and your LLM service**:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
transport.input() → stt → context_aggregator.user()
|
|
26
|
-
→ MemorySyncMemoryService ← enriches + captures here
|
|
27
|
-
→ llm → tts → transport.output() → context_aggregator.assistant()
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
```python
|
|
31
|
-
from pipecat_memorysync import MemorySyncMemoryService
|
|
32
|
-
|
|
33
|
-
memory = MemorySyncMemoryService(
|
|
34
|
-
api_key="ms_...", # or MEMORYSYNC_API_KEY env var
|
|
35
|
-
user_id="caller-42", # stable end-user id
|
|
36
|
-
session_id="call-123", # optional: scope to this call
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
pipeline = Pipeline([
|
|
40
|
-
transport.input(),
|
|
41
|
-
stt,
|
|
42
|
-
context_aggregator.user(),
|
|
43
|
-
memory,
|
|
44
|
-
llm,
|
|
45
|
-
tts,
|
|
46
|
-
transport.output(),
|
|
47
|
-
context_aggregator.assistant(),
|
|
48
|
-
])
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Every `LLMContextFrame` that flows through is enriched with relevant memories
|
|
52
|
-
(as a system message) and
|
|
53
|
-
enriched or not, on time.
|
|
54
|
-
|
|
55
|
-
## Design guarantees
|
|
56
|
-
|
|
57
|
-
- **Budgeted recall.** Enrichment runs under a hard timeout (default
|
|
58
|
-
**1.2 s**). A slow or dead memory backend means an unenriched frame, never a
|
|
59
|
-
stalled voice reply.
|
|
60
|
-
- **Immediate, loss-proof capture.** Every new message is sent the
|
|
61
|
-
its frame passes — the current utterance is in flight BEFORE the
|
|
62
|
-
replies, so a disconnect can never lose it.
|
|
63
|
-
extraction happen server-side
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
re-
|
|
69
|
-
- **Injection exclusion.** The memory block this service adds is never
|
|
70
|
-
back as a new
|
|
71
|
-
- **Graceful end, salvaged abort.** On `EndFrame`, queued
|
|
72
|
-
window (3 s) to land before the pipeline stops — the
|
|
73
|
-
not lost. On `CancelFrame`, the frame is pushed first and
|
|
74
|
-
salvage window.
|
|
75
|
-
- **Failure-proof.** HTTP errors, quota limits, and timeouts all degrade to
|
|
76
|
-
"no memories this turn". Nothing propagates into the pipeline.
|
|
77
|
-
|
|
78
|
-
## Running the example
|
|
79
|
-
|
|
80
|
-
A single-file voice agent that remembers callers across calls lives in
|
|
81
|
-
[`examples/foundational.py`](examples/foundational.py):
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
uv add pipecat-memorysync "pipecat-ai[deepgram,cartesia,openai,silero,runner,webrtc]"
|
|
85
|
-
|
|
86
|
-
export MEMORYSYNC_API_KEY=ms_... # https://app.memorysync.io
|
|
87
|
-
export DEEPGRAM_API_KEY=...
|
|
88
|
-
export CARTESIA_API_KEY=...
|
|
89
|
-
export OPENAI_API_KEY=...
|
|
90
|
-
|
|
91
|
-
python examples/foundational.py
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Open `http://localhost:7860/client`, tell the bot your name and a preference,
|
|
95
|
-
hang up, and connect again — it remembers.
|
|
96
|
-
|
|
97
|
-
## Configuration (`InputParams`)
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
from pipecat_memorysync import MemorySyncMemoryService
|
|
101
|
-
|
|
102
|
-
memory = MemorySyncMemoryService(
|
|
103
|
-
api_key="ms_...",
|
|
104
|
-
user_id="caller-42",
|
|
105
|
-
params=MemorySyncMemoryService.InputParams(
|
|
106
|
-
top_k=5, # memories injected per turn
|
|
107
|
-
recall_timeout=1.2, # hard budget, seconds
|
|
108
|
-
add_as_system_message=True,
|
|
109
|
-
position="end", # where the memory block lands in the context
|
|
110
|
-
min_prompt_chars=8, # skip enrichment for shorter user prompts
|
|
111
|
-
),
|
|
112
|
-
)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
| Param | Default | Meaning |
|
|
116
|
-
| --- | --- | --- |
|
|
117
|
-
| `top_k` | `5` | Memories injected per turn |
|
|
118
|
-
| `recall_timeout` | `1.2` | Hard recall budget in seconds |
|
|
119
|
-
| `system_prompt` | (guarded header) | Prefix line for the injected block; also the capture-exclusion marker |
|
|
120
|
-
| `add_as_system_message` | `True` | Inject as `system` (else appended to the latest user message) |
|
|
121
|
-
| `position` | `"end"` | `"start"` or `"end"` of the message list |
|
|
122
|
-
| `min_prompt_chars` | `8` | Skip recall for trivial prompts |
|
|
123
|
-
|
|
124
|
-
## Semantics worth knowing
|
|
125
|
-
|
|
126
|
-
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
1
|
+
# memorysync-pipecat
|
|
2
|
+
|
|
3
|
+
[MemorySync](https://memorysync.io) for [Pipecat](https://github.com/pipecat-ai/pipecat) —
|
|
4
|
+
long-term memory for voice pipelines that never stalls a reply and never
|
|
5
|
+
re-sends a turn it already sent. PyPI package: **`pipecat-memorysync`**.
|
|
6
|
+
|
|
7
|
+
Built and maintained by the [MemorySync](https://memorysync.io) team —
|
|
8
|
+
MemorySync is our product, and this integration is actively maintained
|
|
9
|
+
alongside it.
|
|
10
|
+
|
|
11
|
+
**Tested with Pipecat v1.8.1** (`pipecat-ai>=1.0.0,<2`).
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install pipecat-memorysync
|
|
15
|
+
# or
|
|
16
|
+
uv add pipecat-memorysync
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Where it sits
|
|
20
|
+
|
|
21
|
+
`MemorySyncMemoryService` is a `FrameProcessor`. Place it **between your
|
|
22
|
+
context aggregator and your LLM service**:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
transport.input() → stt → context_aggregator.user()
|
|
26
|
+
→ MemorySyncMemoryService ← enriches + captures here
|
|
27
|
+
→ llm → tts → transport.output() → context_aggregator.assistant()
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from pipecat_memorysync import MemorySyncMemoryService
|
|
32
|
+
|
|
33
|
+
memory = MemorySyncMemoryService(
|
|
34
|
+
api_key="ms_...", # or MEMORYSYNC_API_KEY env var
|
|
35
|
+
user_id="caller-42", # stable end-user id
|
|
36
|
+
session_id="call-123", # optional: scope to this call
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
pipeline = Pipeline([
|
|
40
|
+
transport.input(),
|
|
41
|
+
stt,
|
|
42
|
+
context_aggregator.user(),
|
|
43
|
+
memory,
|
|
44
|
+
llm,
|
|
45
|
+
tts,
|
|
46
|
+
transport.output(),
|
|
47
|
+
context_aggregator.assistant(),
|
|
48
|
+
])
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every `LLMContextFrame` that flows through is enriched with relevant memories
|
|
52
|
+
(as a system message) and its **new** user turns are sent to fact extraction —
|
|
53
|
+
then pushed on, enriched or not, on time.
|
|
54
|
+
|
|
55
|
+
## Design guarantees
|
|
56
|
+
|
|
57
|
+
- **Budgeted recall.** Enrichment runs under a hard timeout (default
|
|
58
|
+
**1.2 s**). A slow or dead memory backend means an unenriched frame, never a
|
|
59
|
+
stalled voice reply.
|
|
60
|
+
- **Immediate, loss-proof capture.** Every new user message is sent the
|
|
61
|
+
moment its frame passes — the current utterance is in flight BEFORE the
|
|
62
|
+
LLM replies, so a disconnect can never lose it. Filler filtering and fact
|
|
63
|
+
extraction happen server-side: only the durable facts in what the caller
|
|
64
|
+
said are stored as memories, not the turn text.
|
|
65
|
+
- **Delta-only capture.** Only user messages *not seen before* are sent,
|
|
66
|
+
tracked by deterministic idempotency seeds; a retried turn is recognised
|
|
67
|
+
server-side and not extracted twice. Growing a 50-message context does not
|
|
68
|
+
re-send 50 messages per turn.
|
|
69
|
+
- **Injection exclusion.** The memory block this service adds is never sent
|
|
70
|
+
back as a new turn.
|
|
71
|
+
- **Graceful end, salvaged abort.** On `EndFrame`, queued sends get a bounded
|
|
72
|
+
window (3 s) to land before the pipeline stops — the caller's last words are
|
|
73
|
+
not lost. On `CancelFrame`, the frame is pushed first and sends get a brief
|
|
74
|
+
salvage window.
|
|
75
|
+
- **Failure-proof.** HTTP errors, quota limits, and timeouts all degrade to
|
|
76
|
+
"no memories this turn". Nothing propagates into the pipeline.
|
|
77
|
+
|
|
78
|
+
## Running the example
|
|
79
|
+
|
|
80
|
+
A single-file voice agent that remembers callers across calls lives in
|
|
81
|
+
[`examples/foundational.py`](examples/foundational.py):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
uv add pipecat-memorysync "pipecat-ai[deepgram,cartesia,openai,silero,runner,webrtc]"
|
|
85
|
+
|
|
86
|
+
export MEMORYSYNC_API_KEY=ms_... # https://app.memorysync.io
|
|
87
|
+
export DEEPGRAM_API_KEY=...
|
|
88
|
+
export CARTESIA_API_KEY=...
|
|
89
|
+
export OPENAI_API_KEY=...
|
|
90
|
+
|
|
91
|
+
python examples/foundational.py
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Open `http://localhost:7860/client`, tell the bot your name and a preference,
|
|
95
|
+
hang up, and connect again — it remembers.
|
|
96
|
+
|
|
97
|
+
## Configuration (`InputParams`)
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from pipecat_memorysync import MemorySyncMemoryService
|
|
101
|
+
|
|
102
|
+
memory = MemorySyncMemoryService(
|
|
103
|
+
api_key="ms_...",
|
|
104
|
+
user_id="caller-42",
|
|
105
|
+
params=MemorySyncMemoryService.InputParams(
|
|
106
|
+
top_k=5, # memories injected per turn
|
|
107
|
+
recall_timeout=1.2, # hard budget, seconds
|
|
108
|
+
add_as_system_message=True,
|
|
109
|
+
position="end", # where the memory block lands in the context
|
|
110
|
+
min_prompt_chars=8, # skip enrichment for shorter user prompts
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| Param | Default | Meaning |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
| `top_k` | `5` | Memories injected per turn |
|
|
118
|
+
| `recall_timeout` | `1.2` | Hard recall budget in seconds |
|
|
119
|
+
| `system_prompt` | (guarded header) | Prefix line for the injected block; also the capture-exclusion marker |
|
|
120
|
+
| `add_as_system_message` | `True` | Inject as `system` (else appended to the latest user message) |
|
|
121
|
+
| `position` | `"end"` | `"start"` or `"end"` of the message list |
|
|
122
|
+
| `min_prompt_chars` | `8` | Skip recall for trivial prompts |
|
|
123
|
+
|
|
124
|
+
## Semantics worth knowing
|
|
125
|
+
|
|
126
|
+
- Only the caller's (**user**) turns are sent, as plain text with
|
|
127
|
+
`role: "user"`. The server extracts the durable facts they contain and
|
|
128
|
+
stores only those, tagged with `session_id: "pipecat::<session>"`.
|
|
129
|
+
Assistant replies are not sent — they are not stored as memories — and
|
|
130
|
+
filler such as "ok" or "thanks" stores nothing.
|
|
131
|
+
- Idempotency seeds let the server recognise retries and reconnects: the same
|
|
132
|
+
turn is not extracted twice.
|
|
133
|
+
- Free-tier quota exhaustion is silent by design (empty recall, accepted-but-
|
|
134
|
+
skipped sends); evaluation keys surface strict `429`s instead.
|
|
135
|
+
- The service is reusable across pipeline runs; call `await memory.aclose()`
|
|
136
|
+
from application shutdown if you want an explicit flush + client close.
|
|
137
|
+
|
|
138
|
+
## Development
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python -m venv venv && venv/Scripts/pip install -e . pipecat-ai pytest pytest-asyncio
|
|
142
|
+
venv/Scripts/python -m pytest tests -q # 19 tests, run via pipecat's official test harness
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Docs
|
|
146
|
+
|
|
147
|
+
Full guide: [docs.memorysync.io/guides/pipecat](https://docs.memorysync.io/guides/pipecat)
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT
|