Ingest a response and return a list of SaveItem objects to persist. Depending on the event type this will either: - Flush and emit an immediate item (for immediate events), or - Accumulate buffered chunks and emit an upsert SaveItem with the current aggregated payl
(self, resp: BaseResponse)
| 143 | return resp |
| 144 | |
| 145 | def ingest(self, resp: BaseResponse) -> List[SaveItem]: |
| 146 | """Ingest a response and return a list of SaveItem objects to persist. |
| 147 | |
| 148 | Depending on the event type this will either: |
| 149 | - Flush and emit an immediate item (for immediate events), or |
| 150 | - Accumulate buffered chunks and emit an upsert SaveItem with the |
| 151 | current aggregated payload for the paragraph entry. |
| 152 | |
| 153 | Returns: |
| 154 | A list of SaveItem objects that should be persisted by the caller. |
| 155 | """ |
| 156 | data: UnifiedResponseData = resp.data |
| 157 | ev = resp.event |
| 158 | |
| 159 | ctx = ( |
| 160 | data.conversation_id, |
| 161 | data.thread_id, |
| 162 | data.task_id, |
| 163 | ) |
| 164 | out: List[SaveItem] = [] |
| 165 | |
| 166 | # Immediate: write-through, but treat as paragraph boundary for buffered keys |
| 167 | if ev in self._immediate_events: |
| 168 | # Flush buffered aggregates for this context before the immediate item |
| 169 | conv_id, th_id, tk_id = ctx |
| 170 | keys_to_flush = self._collect_task_keys(conv_id, th_id, tk_id) |
| 171 | out.extend(self._finalize_keys(keys_to_flush)) |
| 172 | # Now write the immediate item |
| 173 | out.append(self._make_save_item_from_response(resp)) |
| 174 | return out |
| 175 | |
| 176 | # Buffered: accumulate by (ctx + event) |
| 177 | if ev in self._buffered_events: |
| 178 | key: BufferKey = (*ctx, ev) |
| 179 | entry = self._buffers.get(key) |
| 180 | if not entry: |
| 181 | # If annotate() wasn't called, create an entry now. |
| 182 | entry = BufferEntry(role=data.role, agent_name=data.agent_name) |
| 183 | self._buffers[key] = entry |
| 184 | elif entry.agent_name is None and data.agent_name: |
| 185 | entry.agent_name = data.agent_name |
| 186 | |
| 187 | # Extract text content from payload |
| 188 | payload = data.payload |
| 189 | text = None |
| 190 | if isinstance(payload, BaseResponseDataPayload): |
| 191 | text = payload.content or "" |
| 192 | elif isinstance(payload, BaseModel): |
| 193 | # Fallback: serialize whole payload |
| 194 | text = payload.model_dump_json(exclude_none=True) |
| 195 | elif isinstance(payload, str): |
| 196 | text = payload |
| 197 | else: |
| 198 | text = "" |
| 199 | |
| 200 | if text: |
| 201 | entry.append(text) |
| 202 | # Always upsert current aggregate (no size-based rotation) |