@vibes.diy/api-svc 2.2.7 → 2.2.9

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.
@@ -6,10 +6,11 @@ import { type } from "arktype";
6
6
  import { checkAuth } from "../check-auth.js";
7
7
  import { unwrapMsgBase, wrapMsgBase } from "../unwrap-msg-base.js";
8
8
  import { and, desc, eq } from "drizzle-orm/sql/expressions";
9
- import { applyEdits, createStatsCollector, createLineStream, createDataStream, createSseStream, createDeltaStream, createSectionsStream, isBlockEnd, isCodeBegin, isCodeEnd, isCodeLine, isToplevelLine, parseFenceBody, FileSystemRef, isBlockStreamMsg, isBlockImage, } from "@vibes.diy/call-ai-v2";
10
- import { makeBaseSystemPrompt, resolveEffectiveModel } from "@vibes.diy/prompts";
9
+ import { applyEdits, createStatsCollector, createLineStream, createDataStream, createSseStream, createDeltaStream, createSectionsStream, isBlockEnd, isCodeBegin, isCodeEnd, isCodeLine, isDeltaLine, isToplevelLine, parseFenceBody, FileSystemRef, isBlockStreamMsg, isBlockImage, } from "@vibes.diy/call-ai-v2";
10
+ import { getRecoveryAddendum, getRecoveryStitchAddendum, makeBaseSystemPrompt, resolveEffectiveModel } from "@vibes.diy/prompts";
11
11
  import { ensureAppSlugItem } from "./ensure-app-slug-item.js";
12
12
  import { getModelDefaults } from "../intern/get-model-defaults.js";
13
+ import { buildRecoveryRequest, buildTruncatedEvent, shouldAttemptRecovery, updateRecoveryCounter, } from "../intern/recovery.js";
13
14
  export function createPromptAssetFetch(deps) {
14
15
  return async (url, _init) => {
15
16
  const uri = URI.from(url);
@@ -94,6 +95,97 @@ export function resolveCodeBlocksToFileSystem(blocks, seed) {
94
95
  }
95
96
  return result;
96
97
  }
98
+ function normalizeFilename(rawPath) {
99
+ const path = rawPath ?? "App.jsx";
100
+ return path.startsWith("/") ? path : `/${path}`;
101
+ }
102
+ function searchPrefixOf(search) {
103
+ const firstLine = search.split("\n").find((l) => l.trim().length > 0) ?? "";
104
+ return firstLine.length > 80 ? `${firstLine.slice(0, 80)}…` : firstLine;
105
+ }
106
+ export function createStreamingResolver(deps) {
107
+ const vfs = new Map();
108
+ const seedFor = (filename, rawPath) => {
109
+ return deps.seed.get(filename) ?? deps.seed.get(rawPath) ?? "";
110
+ };
111
+ return {
112
+ observeBlock(block) {
113
+ const rawPath = block.begin.path ?? "App.jsx";
114
+ const filename = normalizeFilename(rawPath);
115
+ const current = vfs.has(filename) ? (vfs.get(filename) ?? "") : seedFor(filename, rawPath);
116
+ const parsed = parseFenceBody(block.lines.map((l) => l.line));
117
+ const errors = [];
118
+ for (const fenceErr of parsed.errors) {
119
+ const evt = {
120
+ chatId: deps.chatId,
121
+ promptId: deps.promptId,
122
+ blockId: block.end.blockId,
123
+ sectionId: block.end.sectionId,
124
+ kind: "fence-parse",
125
+ reason: fenceErr.kind,
126
+ };
127
+ deps.onApplyError(evt);
128
+ errors.push(evt);
129
+ }
130
+ const applied = applyEdits(current, parsed.edits);
131
+ for (const applyErr of applied.errors) {
132
+ const evt = {
133
+ chatId: deps.chatId,
134
+ promptId: deps.promptId,
135
+ blockId: block.end.blockId,
136
+ sectionId: block.end.sectionId,
137
+ kind: "apply",
138
+ reason: applyErr.reason,
139
+ searchPrefix: searchPrefixOf(applyErr.search),
140
+ };
141
+ deps.onApplyError(evt);
142
+ errors.push(evt);
143
+ }
144
+ vfs.set(filename, applied.content);
145
+ return { path: filename, errors };
146
+ },
147
+ getVfs() {
148
+ return new Map(vfs);
149
+ },
150
+ };
151
+ }
152
+ export function createBlockAccumulator() {
153
+ const open = new Map();
154
+ return {
155
+ ingest(msg) {
156
+ if (isCodeBegin(msg)) {
157
+ open.set(msg.blockId, { begin: msg, lines: [] });
158
+ return undefined;
159
+ }
160
+ if (isCodeLine(msg)) {
161
+ open.get(msg.blockId)?.lines.push(msg);
162
+ return undefined;
163
+ }
164
+ if (isCodeEnd(msg)) {
165
+ const acc = open.get(msg.blockId);
166
+ if (!acc)
167
+ return undefined;
168
+ open.delete(msg.blockId);
169
+ return { begin: acc.begin, lines: acc.lines, end: msg };
170
+ }
171
+ return undefined;
172
+ },
173
+ };
174
+ }
175
+ export function logApplyError(logger, evt) {
176
+ logger
177
+ .Debug()
178
+ .Any({
179
+ chatId: evt.chatId,
180
+ promptId: evt.promptId,
181
+ blockId: evt.blockId,
182
+ sectionId: evt.sectionId,
183
+ kind: evt.kind,
184
+ reason: evt.reason,
185
+ ...(evt.searchPrefix === undefined ? {} : { searchPrefix: evt.searchPrefix }),
186
+ })
187
+ .Msg("apply-error");
188
+ }
97
189
  async function appendBlockEvent({ ctx, vctx, req, promptId, blockSeq, evt, emitMode = "store", }) {
98
190
  if (isBlockImage(evt)) {
99
191
  const imgEvt = evt;
@@ -156,6 +248,7 @@ export async function handlePromptContext({ vctx, req, resChat, promptId, blockS
156
248
  const code = [];
157
249
  const sections = [];
158
250
  const collectedMsgs = [...iCollectedMsgs];
251
+ const blockAcc = createBlockAccumulator();
159
252
  for (const msg of collectedMsgs) {
160
253
  if (!isBlockStreamMsg(msg)) {
161
254
  continue;
@@ -172,25 +265,9 @@ export async function handlePromptContext({ vctx, req, resChat, promptId, blockS
172
265
  sqlVal = sections[sections.length - 1];
173
266
  }
174
267
  sqlVal.blocks.push(msg);
175
- if (isCodeBegin(msg)) {
176
- code.push({ begin: msg, lines: [] });
177
- }
178
- else if (isCodeLine(msg)) {
179
- if (code.length === 0) {
180
- console.warn("Received code line without a preceding code begin:", msg);
181
- }
182
- else {
183
- code[code.length - 1].lines.push(msg);
184
- }
185
- }
186
- else if (isCodeEnd(msg)) {
187
- if (code.length === 0) {
188
- console.warn("Received code end without a preceding code begin:", msg);
189
- }
190
- else {
191
- code[code.length - 1].end = msg;
192
- }
193
- }
268
+ const closed = blockAcc.ingest(msg);
269
+ if (closed)
270
+ code.push({ begin: closed.begin, lines: [...closed.lines], end: closed.end });
194
271
  }
195
272
  if (code.length > 0 && (resChat.mode === "chat" || isPromptFSStyle(resChat.mode))) {
196
273
  let resolvedFileSystem;
@@ -325,10 +402,13 @@ async function injectSystemPrompt(vctx, chatId, model) {
325
402
  }
326
403
  const conversationMessages = reconstructConversationMessages(allSectionMsgs);
327
404
  const { skills, title } = await loadActiveSettings(vctx, chatId);
405
+ const priorFs = await loadPriorFileSystem(vctx, chatId);
406
+ const isInitial = priorFs.size === 0;
328
407
  const systemPrompt = await exception2Result(async () => makeBaseSystemPrompt(await resolveEffectiveModel({ model }, {}), {
329
408
  skills,
330
409
  title,
331
410
  demoData: false,
411
+ variant: isInitial ? "initial" : "continuation",
332
412
  pkgBaseUrl: promptsPkgBaseUrl(vctx.params.pkgRepos.workspace),
333
413
  fetch: createPromptAssetFetch({ fetchAsset: vctx.fetchAsset }),
334
414
  }));
@@ -483,9 +563,10 @@ async function handlerLlmRequest({ scope, ctx, vctx, blockSeq, req, resChat, pro
483
563
  ...(isInitialTurn && req.mode === "chat" ? { verbosity: "low" } : {}),
484
564
  ...(req.mode === "img" ? { modalities: ["text", "image"] } : {}),
485
565
  };
566
+ const abort = new AbortController();
486
567
  const res = await scope
487
568
  .evalResult(async () => {
488
- const res = await vctx.llmRequest(llmReq);
569
+ const res = await vctx.llmRequest(llmReq, { signal: abort.signal });
489
570
  if (!res.ok) {
490
571
  return Result.Err(`LLM request failed with status ${res.status} :${llmReq.model} : ${res.statusText}`);
491
572
  }
@@ -495,7 +576,7 @@ async function handlerLlmRequest({ scope, ctx, vctx, blockSeq, req, resChat, pro
495
576
  return Result.Ok(res);
496
577
  })
497
578
  .do();
498
- return { res, blockSeq };
579
+ return { res, blockSeq, llmReq, abort };
499
580
  }
500
581
  async function handleProdiaImageRequest({ scope, ctx, vctx, req, promptId, blockSeq, resolvedModel, }) {
501
582
  const prodiaToken = vctx.prodiaToken;
@@ -697,27 +778,17 @@ async function handleEndMsg({ collectedMsgs, vctx, req, ctx, resChat, promptId,
697
778
  }
698
779
  return Result.Ok(blockSeq);
699
780
  }
700
- async function handleLlmResponse({ scope, vctx, req, ctx, res, resChat, promptId, blockSeq, }) {
781
+ async function handleLlmResponse({ scope, vctx, req, ctx, res, resChat, promptId, blockSeq, llmReq, abort, }) {
701
782
  await scope
702
783
  .evalResult(async () => {
703
- const pipeline = res
704
- .body.pipeThrough(createStatsCollector(promptId, 1000))
705
- .pipeThrough(createLineStream(promptId))
706
- .pipeThrough(createDataStream(promptId))
707
- .pipeThrough(createSseStream(promptId))
708
- .pipeThrough(createDeltaStream(promptId, () => vctx.sthis.nextId(12).str))
709
- .pipeThrough(createSectionsStream(promptId, () => vctx.sthis.nextId(12).str));
710
- const reader = pipeline.getReader();
711
784
  let collectedMsgs;
712
- let chatCtx;
713
785
  for (const conn of vctx.connections) {
714
786
  const tChatCtx = conn.chatIds.get(req.chatId);
715
787
  if (tChatCtx) {
716
- chatCtx = tChatCtx;
717
- const promptIdCtx = chatCtx.promptIds.get(promptId);
788
+ const promptIdCtx = tChatCtx.promptIds.get(promptId);
718
789
  if (!promptIdCtx) {
719
790
  collectedMsgs = [];
720
- chatCtx.promptIds.set(promptId, {
791
+ tChatCtx.promptIds.set(promptId, {
721
792
  blocks: collectedMsgs,
722
793
  promptId,
723
794
  type: "vibes.diy.section-event",
@@ -734,40 +805,301 @@ async function handleLlmResponse({ scope, vctx, req, ctx, res, resChat, promptId
734
805
  if (!collectedMsgs) {
735
806
  return Result.Err(`Chat context not found for chatId: ${req.chatId}`);
736
807
  }
808
+ const seedForResolver = await loadPriorFileSystem(vctx, req.chatId);
809
+ const resolverLogger = ensureLogger(vctx.sthis, "streamingResolver");
810
+ const recoveryLogger = ensureLogger(vctx.sthis, "applyRecovery");
811
+ const streamingResolver = createStreamingResolver({
812
+ chatId: req.chatId,
813
+ promptId,
814
+ seed: seedForResolver,
815
+ onApplyError: (evt) => logApplyError(resolverLogger, evt),
816
+ });
817
+ const blockAcc = createBlockAccumulator();
818
+ let recoveryCounter = { consecutiveFruitless: 0 };
819
+ let isRecoveryStream = false;
820
+ let currentRes = res;
821
+ let currentAbort = abort;
737
822
  while (true) {
738
- const { done, value } = await reader.read();
739
- if (done) {
740
- break;
741
- }
742
- if (!isBlockEnd(value)) {
743
- if (!isBlockStreamMsg(value)) {
823
+ let streamMadeProgress = false;
824
+ const partialBuffer = { text: "", safeCut: 0, lastReplaceFileLines: undefined };
825
+ const captureDeltas = new TransformStream({
826
+ transform(msg, controller) {
827
+ if (isDeltaLine(msg))
828
+ partialBuffer.text += msg.content;
829
+ controller.enqueue(msg);
830
+ },
831
+ });
832
+ const pipeline = currentRes
833
+ .body.pipeThrough(createStatsCollector(promptId, 1000))
834
+ .pipeThrough(createLineStream(promptId))
835
+ .pipeThrough(createDataStream(promptId))
836
+ .pipeThrough(createSseStream(promptId))
837
+ .pipeThrough(createDeltaStream(promptId, () => vctx.sthis.nextId(12).str))
838
+ .pipeThrough(captureDeltas)
839
+ .pipeThrough(createSectionsStream(promptId, () => vctx.sthis.nextId(12).str));
840
+ const reader = pipeline.getReader();
841
+ let recoverHint = null;
842
+ let drainOnly = false;
843
+ readLoop: while (true) {
844
+ const rRead = await exception2Result(() => reader.read());
845
+ if (rRead.isErr()) {
846
+ const intentional = drainOnly || currentAbort.signal.aborted;
847
+ if (!intentional) {
848
+ recoveryLogger
849
+ .Info()
850
+ .Any("event", {
851
+ chatId: req.chatId,
852
+ promptId,
853
+ err: String(rRead.Err()),
854
+ })
855
+ .Msg("upstream-read-failed");
856
+ }
857
+ break readLoop;
858
+ }
859
+ const { done, value } = rRead.Ok();
860
+ if (done)
861
+ break readLoop;
862
+ if (drainOnly)
744
863
  continue;
864
+ if (!isBlockEnd(value)) {
865
+ if (!isBlockStreamMsg(value))
866
+ continue;
867
+ const closed = blockAcc.ingest(value);
868
+ const applyResult = closed ? streamingResolver.observeBlock(closed) : undefined;
869
+ const isFailedCodeEnd = closed !== undefined && applyResult !== undefined && applyResult.errors.length > 0;
870
+ if (isFailedCodeEnd) {
871
+ const first = applyResult.errors[0];
872
+ const truncateEvt = buildTruncatedEvent({
873
+ closed,
874
+ firstError: first,
875
+ errorCount: applyResult.errors.length,
876
+ promptId,
877
+ blockSeq,
878
+ now: new Date(),
879
+ });
880
+ collectedMsgs.push(truncateEvt);
881
+ const r = await appendBlockEvent({
882
+ ctx,
883
+ vctx,
884
+ req,
885
+ promptId,
886
+ blockSeq: blockSeq++,
887
+ evt: truncateEvt,
888
+ emitMode: "emit-only",
889
+ });
890
+ if (r.isErr()) {
891
+ return Result.Err(r);
892
+ }
893
+ recoverHint = {
894
+ partial: partialBuffer.text.slice(0, partialBuffer.safeCut),
895
+ focusPath: applyResult.path,
896
+ blockId: closed.end.blockId,
897
+ sectionId: closed.end.sectionId,
898
+ reason: first.reason,
899
+ kind: first.kind,
900
+ errorCount: applyResult.errors.length,
901
+ lastReplaceFileLines: partialBuffer.lastReplaceFileLines,
902
+ };
903
+ currentAbort.abort();
904
+ drainOnly = true;
905
+ continue;
906
+ }
907
+ collectedMsgs.push(value);
908
+ const r = await appendBlockEvent({
909
+ ctx,
910
+ vctx,
911
+ req,
912
+ promptId,
913
+ blockSeq: blockSeq++,
914
+ evt: value,
915
+ emitMode: "emit-only",
916
+ });
917
+ if (r.isErr()) {
918
+ return Result.Err(r);
919
+ }
920
+ if (closed !== undefined && applyResult !== undefined && applyResult.errors.length === 0) {
921
+ partialBuffer.safeCut = partialBuffer.text.length;
922
+ streamMadeProgress = true;
923
+ const wasReplace = closed.lines.some((l) => l.line.startsWith("<<<<<<< SEARCH"));
924
+ if (wasReplace) {
925
+ const resolvedText = streamingResolver.getVfs().get(applyResult.path);
926
+ partialBuffer.lastReplaceFileLines = resolvedText ? resolvedText.split("\n").length : undefined;
927
+ }
928
+ else {
929
+ partialBuffer.lastReplaceFileLines = undefined;
930
+ }
931
+ }
745
932
  }
746
- collectedMsgs.push(value);
747
- const r = await appendBlockEvent({
748
- ctx,
933
+ else {
934
+ collectedMsgs.push(value);
935
+ const x = await handleEndMsg({ collectedMsgs, vctx, req, ctx, resChat, promptId, value, blockSeq });
936
+ if (x.isErr())
937
+ return Result.Err(x);
938
+ blockSeq = x.Ok();
939
+ collectedMsgs.splice(0, collectedMsgs.length);
940
+ }
941
+ }
942
+ if (isRecoveryStream) {
943
+ recoveryCounter = updateRecoveryCounter(recoveryCounter, { madeProgress: streamMadeProgress });
944
+ recoveryLogger
945
+ .Debug()
946
+ .Any("event", {
947
+ chatId: req.chatId,
948
+ promptId,
949
+ madeProgress: streamMadeProgress,
950
+ consecutiveFruitless: recoveryCounter.consecutiveFruitless,
951
+ })
952
+ .Msg("recovery-stream-end");
953
+ }
954
+ if (recoverHint === null) {
955
+ return Result.Ok();
956
+ }
957
+ if (!shouldAttemptRecovery(recoveryCounter)) {
958
+ recoveryLogger
959
+ .Info()
960
+ .Any("event", {
961
+ chatId: req.chatId,
962
+ promptId,
963
+ blockId: recoverHint.blockId,
964
+ consecutiveFruitless: recoveryCounter.consecutiveFruitless,
965
+ })
966
+ .Msg("recovery-exhausted");
967
+ const exhaustedEnd = {
968
+ type: "block.end",
969
+ blockId: recoverHint.blockId,
970
+ streamId: promptId,
971
+ seq: blockSeq,
972
+ blockNr: 0,
973
+ timestamp: new Date(),
974
+ stats: {
975
+ toplevel: { lines: 0, bytes: 0 },
976
+ code: { lines: 0, bytes: 0 },
977
+ image: { lines: 0, bytes: 0 },
978
+ total: { lines: 0, bytes: 0 },
979
+ },
980
+ usage: {
981
+ given: [],
982
+ calculated: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
983
+ },
984
+ };
985
+ collectedMsgs.push(exhaustedEnd);
986
+ const xEnd = await handleEndMsg({
987
+ collectedMsgs,
749
988
  vctx,
750
989
  req,
990
+ ctx,
991
+ resChat,
751
992
  promptId,
752
- blockSeq: blockSeq++,
753
- evt: value,
754
- emitMode: "emit-only",
993
+ value: exhaustedEnd,
994
+ blockSeq,
755
995
  });
756
- if (r.isErr()) {
757
- return Result.Err(r);
758
- }
996
+ if (xEnd.isErr())
997
+ return Result.Err(xEnd);
998
+ return Result.Ok();
759
999
  }
760
- else {
761
- collectedMsgs.push(value);
762
- const x = await handleEndMsg({ collectedMsgs, vctx, req, ctx, resChat, promptId, value, blockSeq });
763
- if (x.isErr()) {
764
- return Result.Err(x);
765
- }
766
- blockSeq = x.Ok();
767
- collectedMsgs.splice(0, collectedMsgs.length);
1000
+ const stitchMode = recoveryCounter.consecutiveFruitless === 2;
1001
+ recoveryLogger
1002
+ .Debug()
1003
+ .Any("event", {
1004
+ chatId: req.chatId,
1005
+ promptId,
1006
+ blockId: recoverHint.blockId,
1007
+ sectionId: recoverHint.sectionId,
1008
+ path: recoverHint.focusPath,
1009
+ reason: recoverHint.reason,
1010
+ kind: recoverHint.kind,
1011
+ errorCount: recoverHint.errorCount,
1012
+ consecutiveFruitless: recoveryCounter.consecutiveFruitless,
1013
+ mode: stitchMode ? "stitch" : "continue",
1014
+ })
1015
+ .Msg("recovery-start");
1016
+ const pkgBaseUrl = promptsPkgBaseUrl(vctx.params.pkgRepos.workspace);
1017
+ const fetchOverride = createPromptAssetFetch({ fetchAsset: vctx.fetchAsset });
1018
+ const addendum = await exception2Result(() => stitchMode ? getRecoveryStitchAddendum(pkgBaseUrl, fetchOverride) : getRecoveryAddendum(pkgBaseUrl, fetchOverride));
1019
+ if (addendum.isErr()) {
1020
+ recoveryLogger
1021
+ .Info()
1022
+ .Any("event", { chatId: req.chatId, promptId, err: String(addendum.Err()) })
1023
+ .Msg("recovery-addendum-failed");
1024
+ return Result.Ok();
768
1025
  }
1026
+ const recReq = buildRecoveryRequest({
1027
+ originalRequest: llmReq,
1028
+ recoveryAddendum: addendum.Ok(),
1029
+ vfs: streamingResolver.getVfs(),
1030
+ focusPath: recoverHint.focusPath,
1031
+ assistantPartial: stitchMode ? undefined : recoverHint.partial,
1032
+ lastReplaceFileLines: stitchMode ? undefined : recoverHint.lastReplaceFileLines,
1033
+ });
1034
+ if (recReq.isErr()) {
1035
+ recoveryLogger
1036
+ .Info()
1037
+ .Any("event", {
1038
+ chatId: req.chatId,
1039
+ promptId,
1040
+ err: String(recReq.Err()),
1041
+ originalMessageCount: llmReq.messages.length,
1042
+ originalRoles: llmReq.messages.map((m) => m.role),
1043
+ })
1044
+ .Msg("recovery-build-failed");
1045
+ return Result.Ok();
1046
+ }
1047
+ const recPayload = recReq.Ok();
1048
+ const recMessageCount = recPayload.messages.length;
1049
+ const recRoles = recPayload.messages.map((m) => m.role);
1050
+ const recModel = recPayload.model;
1051
+ const nextAbort = new AbortController();
1052
+ const rNextRes = await exception2Result(() => vctx.llmRequest({ ...recPayload, headers: llmReq.headers }, { signal: nextAbort.signal }));
1053
+ if (rNextRes.isErr()) {
1054
+ recoveryLogger
1055
+ .Info()
1056
+ .Any("event", {
1057
+ chatId: req.chatId,
1058
+ promptId,
1059
+ err: String(rNextRes.Err()),
1060
+ model: recModel,
1061
+ messageCount: recMessageCount,
1062
+ roles: recRoles,
1063
+ })
1064
+ .Msg("recovery-call-failed");
1065
+ return Result.Ok();
1066
+ }
1067
+ const nextRes = rNextRes.Ok();
1068
+ if (!nextRes.ok || !nextRes.body) {
1069
+ const rBody = await exception2Result(() => nextRes.text());
1070
+ const rawBody = rBody.isOk() ? rBody.Ok() : `<read-failed: ${String(rBody.Err())}>`;
1071
+ const bodySnippet = rawBody.length > 2000 ? `${rawBody.slice(0, 2000)}…[+${rawBody.length - 2000}b]` : rawBody;
1072
+ recoveryLogger
1073
+ .Info()
1074
+ .Any("event", {
1075
+ chatId: req.chatId,
1076
+ promptId,
1077
+ status: nextRes.status,
1078
+ statusText: nextRes.statusText,
1079
+ model: recModel,
1080
+ messageCount: recMessageCount,
1081
+ roles: recRoles,
1082
+ bodySnippet,
1083
+ })
1084
+ .Msg("recovery-call-failed");
1085
+ return Result.Ok();
1086
+ }
1087
+ recoveryLogger
1088
+ .Debug()
1089
+ .Any("event", {
1090
+ chatId: req.chatId,
1091
+ promptId,
1092
+ partialBytes: recoverHint.partial.length,
1093
+ focusPath: recoverHint.focusPath,
1094
+ model: recModel,
1095
+ messageCount: recMessageCount,
1096
+ roles: recRoles,
1097
+ })
1098
+ .Msg("recovery-call-started");
1099
+ currentRes = nextRes;
1100
+ currentAbort = nextAbort;
1101
+ isRecoveryStream = true;
769
1102
  }
770
- return Result.Ok();
771
1103
  })
772
1104
  .do();
773
1105
  return blockSeq;
@@ -990,6 +1322,8 @@ export const promptChatSection = {
990
1322
  resChat,
991
1323
  promptId,
992
1324
  blockSeq: res.blockSeq,
1325
+ llmReq: res.llmReq,
1326
+ abort: res.abort,
993
1327
  });
994
1328
  return Result.Ok(finalBlockSeq);
995
1329
  };