| 173 | ].join("\n\n") |
| 174 | |
| 175 | export const make = (dependencies: Dependencies) => { |
| 176 | const config = settings(dependencies.config) |
| 177 | const compactAfterOverflow = Effect.fn("SessionCompaction.compactAfterOverflow")(function* (input: Input) { |
| 178 | const context = input.model.route.defaults.limits?.context |
| 179 | if (context === undefined || context <= 0) return false |
| 180 | const output = input.request.generation?.maxTokens ?? input.model.route.defaults.limits?.output ?? 0 |
| 181 | const selected = select(input.entries, config.tokens) |
| 182 | const previousSummary = input.entries.find((entry) => entry.message.type === "compaction")?.message |
| 183 | if (!selected || (selected.head.length === 0 && previousSummary?.type !== "compaction")) return false |
| 184 | const summaryPrompt = buildPrompt({ |
| 185 | previousSummary: previousSummary?.type === "compaction" ? previousSummary.summary : undefined, |
| 186 | context: [previousSummary?.type === "compaction" ? previousSummary.recent : "", selected.head].filter(Boolean), |
| 187 | }) |
| 188 | const summaryOutput = Math.min(output || SUMMARY_OUTPUT_TOKENS, SUMMARY_OUTPUT_TOKENS) |
| 189 | if (Token.estimate(summaryPrompt) > context - summaryOutput) return false |
| 190 | const messageID = SessionMessage.ID.create() |
| 191 | yield* dependencies.events.publish(SessionEvent.Compaction.Started, { |
| 192 | sessionID: input.sessionID, |
| 193 | messageID, |
| 194 | timestamp: yield* DateTime.now, |
| 195 | reason: "auto", |
| 196 | }) |
| 197 | |
| 198 | const chunks: string[] = [] |
| 199 | let failed = false |
| 200 | const summarized = yield* dependencies.llm |
| 201 | .stream( |
| 202 | LLM.request({ |
| 203 | model: input.model, |
| 204 | messages: [Message.user(summaryPrompt)], |
| 205 | tools: [], |
| 206 | generation: { maxTokens: summaryOutput }, |
| 207 | }), |
| 208 | ) |
| 209 | .pipe( |
| 210 | Stream.runForEach((event) => { |
| 211 | if (LLMEvent.is.providerError(event)) failed = true |
| 212 | if (LLMEvent.is.textDelta(event)) chunks.push(event.text) |
| 213 | return Effect.void |
| 214 | }), |
| 215 | Effect.as(true), |
| 216 | Effect.catchTag("LLM.Error", () => Effect.succeed(false)), |
| 217 | ) |
| 218 | const summary = chunks.join("") |
| 219 | if (!summarized || failed || !summary.trim()) return false |
| 220 | yield* dependencies.events.publish(SessionEvent.Compaction.Ended, { |
| 221 | sessionID: input.sessionID, |
| 222 | messageID, |
| 223 | timestamp: yield* DateTime.now, |
| 224 | reason: "auto", |
| 225 | text: summary, |
| 226 | recent: selected.recent, |
| 227 | }) |
| 228 | return true |
| 229 | }) |
| 230 | const compactIfNeeded = Effect.fn("SessionCompaction.compactIfNeeded")(function* (input: Input) { |
| 231 | if (!config.auto) return false |
| 232 | const context = input.model.route.defaults.limits?.context |