@cloudflare/ai-chat 0.0.4 → 0.0.5

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.
package/dist/index.js CHANGED
@@ -80,6 +80,7 @@ var AIChatAgent = class extends Agent {
80
80
  this._chunkBuffer = [];
81
81
  this._isFlushingChunks = false;
82
82
  this._lastCleanupTime = 0;
83
+ this._pendingResumeConnections = /* @__PURE__ */ new Set();
83
84
  this.sql`create table if not exists cf_ai_chat_agent_messages (
84
85
  id text primary key,
85
86
  message text not null,
@@ -105,9 +106,14 @@ var AIChatAgent = class extends Agent {
105
106
  this._chatMessageAbortControllers = /* @__PURE__ */ new Map();
106
107
  this._restoreActiveStream();
107
108
  const _onConnect = this.onConnect.bind(this);
108
- this.onConnect = async (connection, ctx$1) => {
109
+ this.onConnect = async (connection, ctx) => {
109
110
  if (this._activeStreamId) this._notifyStreamResuming(connection);
110
- return _onConnect(connection, ctx$1);
111
+ return _onConnect(connection, ctx);
112
+ };
113
+ const _onClose = this.onClose.bind(this);
114
+ this.onClose = async (connection, code, reason, wasClean) => {
115
+ this._pendingResumeConnections.delete(connection.id);
116
+ return _onClose(connection, code, reason, wasClean);
111
117
  };
112
118
  const _onMessage = this.onMessage.bind(this);
113
119
  this.onMessage = async (connection, message) => {
@@ -177,6 +183,7 @@ var AIChatAgent = class extends Agent {
177
183
  this._activeStreamId = null;
178
184
  this._activeRequestId = null;
179
185
  this._streamChunkIndex = 0;
186
+ this._pendingResumeConnections.clear();
180
187
  this.messages = [];
181
188
  this._broadcastChatMessage({ type: MessageType.CF_AGENT_CHAT_CLEAR }, [connection.id]);
182
189
  return;
@@ -191,6 +198,7 @@ var AIChatAgent = class extends Agent {
191
198
  return;
192
199
  }
193
200
  if (data.type === MessageType.CF_AGENT_STREAM_RESUME_ACK) {
201
+ this._pendingResumeConnections.delete(connection.id);
194
202
  if (this._activeStreamId && this._activeRequestId && this._activeRequestId === data.id) this._sendStreamChunks(connection, this._activeStreamId, this._activeRequestId);
195
203
  return;
196
204
  }
@@ -274,6 +282,7 @@ var AIChatAgent = class extends Agent {
274
282
  */
275
283
  _notifyStreamResuming(connection) {
276
284
  if (!this._activeStreamId || !this._activeRequestId) return;
285
+ this._pendingResumeConnections.add(connection.id);
277
286
  connection.send(JSON.stringify({
278
287
  type: MessageType.CF_AGENT_STREAM_RESUMING,
279
288
  id: this._activeRequestId
@@ -376,6 +385,7 @@ var AIChatAgent = class extends Agent {
376
385
  this._activeStreamId = null;
377
386
  this._activeRequestId = null;
378
387
  this._streamChunkIndex = 0;
388
+ this._pendingResumeConnections.clear();
379
389
  this._maybeCleanupOldStreams();
380
390
  }
381
391
  /**
@@ -400,7 +410,8 @@ var AIChatAgent = class extends Agent {
400
410
  `;
401
411
  }
402
412
  _broadcastChatMessage(message, exclude) {
403
- this.broadcast(JSON.stringify(message), exclude);
413
+ const allExclusions = [...exclude || [], ...this._pendingResumeConnections];
414
+ this.broadcast(JSON.stringify(message), allExclusions);
404
415
  }
405
416
  /**
406
417
  * Broadcasts a text event for non-SSE responses.
@@ -689,6 +700,374 @@ var AIChatAgent = class extends Agent {
689
700
  }
690
701
  return true;
691
702
  }
703
+ async _streamSSEReply(id, streamId, reader, message, streamCompleted, continuation = false) {
704
+ let activeTextParts = {};
705
+ let activeReasoningParts = {};
706
+ const partialToolCalls = {};
707
+ const { getToolName, isToolUIPart, parsePartialJson } = await import("ai");
708
+ streamCompleted.value = false;
709
+ while (true) {
710
+ const { done, value } = await reader.read();
711
+ if (done) {
712
+ this._completeStream(streamId);
713
+ streamCompleted.value = true;
714
+ this._broadcastChatMessage({
715
+ body: "",
716
+ done: true,
717
+ id,
718
+ type: MessageType.CF_AGENT_USE_CHAT_RESPONSE,
719
+ ...continuation && { continuation: true }
720
+ });
721
+ break;
722
+ }
723
+ const lines = decoder.decode(value).split("\n");
724
+ for (const line of lines) if (line.startsWith("data: ") && line !== "data: [DONE]") try {
725
+ const data = JSON.parse(line.slice(6));
726
+ switch (data.type) {
727
+ case "text-start": {
728
+ const textPart = {
729
+ type: "text",
730
+ text: "",
731
+ providerMetadata: data.providerMetadata,
732
+ state: "streaming"
733
+ };
734
+ activeTextParts[data.id] = textPart;
735
+ message.parts.push(textPart);
736
+ break;
737
+ }
738
+ case "text-delta": {
739
+ const textPart = activeTextParts[data.id];
740
+ textPart.text += data.delta;
741
+ textPart.providerMetadata = data.providerMetadata ?? textPart.providerMetadata;
742
+ break;
743
+ }
744
+ case "text-end": {
745
+ const textPart = activeTextParts[data.id];
746
+ textPart.state = "done";
747
+ textPart.providerMetadata = data.providerMetadata ?? textPart.providerMetadata;
748
+ delete activeTextParts[data.id];
749
+ break;
750
+ }
751
+ case "reasoning-start": {
752
+ const reasoningPart = {
753
+ type: "reasoning",
754
+ text: "",
755
+ providerMetadata: data.providerMetadata,
756
+ state: "streaming"
757
+ };
758
+ activeReasoningParts[data.id] = reasoningPart;
759
+ message.parts.push(reasoningPart);
760
+ break;
761
+ }
762
+ case "reasoning-delta": {
763
+ const reasoningPart = activeReasoningParts[data.id];
764
+ reasoningPart.text += data.delta;
765
+ reasoningPart.providerMetadata = data.providerMetadata ?? reasoningPart.providerMetadata;
766
+ break;
767
+ }
768
+ case "reasoning-end": {
769
+ const reasoningPart = activeReasoningParts[data.id];
770
+ reasoningPart.providerMetadata = data.providerMetadata ?? reasoningPart.providerMetadata;
771
+ reasoningPart.state = "done";
772
+ delete activeReasoningParts[data.id];
773
+ break;
774
+ }
775
+ case "file":
776
+ message.parts.push({
777
+ type: "file",
778
+ mediaType: data.mediaType,
779
+ url: data.url
780
+ });
781
+ break;
782
+ case "source-url":
783
+ message.parts.push({
784
+ type: "source-url",
785
+ sourceId: data.sourceId,
786
+ url: data.url,
787
+ title: data.title,
788
+ providerMetadata: data.providerMetadata
789
+ });
790
+ break;
791
+ case "source-document":
792
+ message.parts.push({
793
+ type: "source-document",
794
+ sourceId: data.sourceId,
795
+ mediaType: data.mediaType,
796
+ title: data.title,
797
+ filename: data.filename,
798
+ providerMetadata: data.providerMetadata
799
+ });
800
+ break;
801
+ case "tool-input-start": {
802
+ const toolInvocations = message.parts.filter(isToolUIPart);
803
+ partialToolCalls[data.toolCallId] = {
804
+ text: "",
805
+ toolName: data.toolName,
806
+ index: toolInvocations.length,
807
+ dynamic: data.dynamic
808
+ };
809
+ if (data.dynamic) this.updateDynamicToolPart(message, {
810
+ toolCallId: data.toolCallId,
811
+ toolName: data.toolName,
812
+ state: "input-streaming",
813
+ input: void 0
814
+ });
815
+ else await this.updateToolPart(message, {
816
+ toolCallId: data.toolCallId,
817
+ toolName: data.toolName,
818
+ state: "input-streaming",
819
+ input: void 0
820
+ });
821
+ break;
822
+ }
823
+ case "tool-input-delta": {
824
+ const partialToolCall = partialToolCalls[data.toolCallId];
825
+ partialToolCall.text += data.inputTextDelta;
826
+ const partialArgs = (await parsePartialJson(partialToolCall.text)).value;
827
+ if (partialToolCall.dynamic) this.updateDynamicToolPart(message, {
828
+ toolCallId: data.toolCallId,
829
+ toolName: partialToolCall.toolName,
830
+ state: "input-streaming",
831
+ input: partialArgs
832
+ });
833
+ else await this.updateToolPart(message, {
834
+ toolCallId: data.toolCallId,
835
+ toolName: partialToolCall.toolName,
836
+ state: "input-streaming",
837
+ input: partialArgs
838
+ });
839
+ break;
840
+ }
841
+ case "tool-input-available":
842
+ if (data.dynamic) this.updateDynamicToolPart(message, {
843
+ toolCallId: data.toolCallId,
844
+ toolName: data.toolName,
845
+ state: "input-available",
846
+ input: data.input,
847
+ providerMetadata: data.providerMetadata
848
+ });
849
+ else await this.updateToolPart(message, {
850
+ toolCallId: data.toolCallId,
851
+ toolName: data.toolName,
852
+ state: "input-available",
853
+ input: data.input,
854
+ providerExecuted: data.providerExecuted,
855
+ providerMetadata: data.providerMetadata
856
+ });
857
+ break;
858
+ case "tool-input-error":
859
+ if (data.dynamic) this.updateDynamicToolPart(message, {
860
+ toolCallId: data.toolCallId,
861
+ toolName: data.toolName,
862
+ state: "output-error",
863
+ input: data.input,
864
+ errorText: data.errorText,
865
+ providerMetadata: data.providerMetadata
866
+ });
867
+ else await this.updateToolPart(message, {
868
+ toolCallId: data.toolCallId,
869
+ toolName: data.toolName,
870
+ state: "output-error",
871
+ input: void 0,
872
+ rawInput: data.input,
873
+ errorText: data.errorText,
874
+ providerExecuted: data.providerExecuted,
875
+ providerMetadata: data.providerMetadata
876
+ });
877
+ break;
878
+ case "tool-output-available":
879
+ if (data.dynamic) {
880
+ const toolInvocation = message.parts.filter((part) => part.type === "dynamic-tool").find((invocation) => invocation.toolCallId === data.toolCallId);
881
+ if (!toolInvocation) throw new Error("Tool invocation not found");
882
+ this.updateDynamicToolPart(message, {
883
+ toolCallId: data.toolCallId,
884
+ toolName: toolInvocation.toolName,
885
+ state: "output-available",
886
+ input: toolInvocation.input,
887
+ output: data.output,
888
+ preliminary: data.preliminary
889
+ });
890
+ } else {
891
+ const toolInvocation = message.parts.filter(isToolUIPart).find((invocation) => invocation.toolCallId === data.toolCallId);
892
+ if (!toolInvocation) throw new Error("Tool invocation not found");
893
+ await this.updateToolPart(message, {
894
+ toolCallId: data.toolCallId,
895
+ toolName: getToolName(toolInvocation),
896
+ state: "output-available",
897
+ input: toolInvocation.input,
898
+ output: data.output,
899
+ providerExecuted: data.providerExecuted,
900
+ preliminary: data.preliminary
901
+ });
902
+ }
903
+ break;
904
+ case "tool-output-error":
905
+ if (data.dynamic) {
906
+ const toolInvocation = message.parts.filter((part) => part.type === "dynamic-tool").find((invocation) => invocation.toolCallId === data.toolCallId);
907
+ if (!toolInvocation) throw new Error("Tool invocation not found");
908
+ this.updateDynamicToolPart(message, {
909
+ toolCallId: data.toolCallId,
910
+ toolName: toolInvocation.toolName,
911
+ state: "output-error",
912
+ input: toolInvocation.input,
913
+ errorText: data.errorText
914
+ });
915
+ } else {
916
+ const toolInvocation = message.parts.filter(isToolUIPart).find((invocation) => invocation.toolCallId === data.toolCallId);
917
+ if (!toolInvocation) throw new Error("Tool invocation not found");
918
+ await this.updateToolPart(message, {
919
+ toolCallId: data.toolCallId,
920
+ toolName: getToolName(toolInvocation),
921
+ state: "output-error",
922
+ input: toolInvocation.input,
923
+ rawInput: "rawInput" in toolInvocation ? toolInvocation.rawInput : void 0,
924
+ errorText: data.errorText
925
+ });
926
+ }
927
+ break;
928
+ case "start-step":
929
+ message.parts.push({ type: "step-start" });
930
+ break;
931
+ case "finish-step":
932
+ activeTextParts = {};
933
+ activeReasoningParts = {};
934
+ break;
935
+ case "start":
936
+ if (data.messageId != null) message.id = data.messageId;
937
+ await this.updateMessageMetadata(message, data.messageMetadata);
938
+ break;
939
+ case "finish":
940
+ await this.updateMessageMetadata(message, data.messageMetadata);
941
+ break;
942
+ case "message-metadata":
943
+ await this.updateMessageMetadata(message, data.messageMetadata);
944
+ break;
945
+ case "error":
946
+ this._broadcastChatMessage({
947
+ error: true,
948
+ body: data.errorText ?? JSON.stringify(data),
949
+ done: false,
950
+ id,
951
+ type: MessageType.CF_AGENT_USE_CHAT_RESPONSE
952
+ });
953
+ break;
954
+ }
955
+ let eventToSend = data;
956
+ if (data.type === "finish" && "finishReason" in data) {
957
+ const { finishReason, ...rest } = data;
958
+ eventToSend = {
959
+ ...rest,
960
+ type: "finish",
961
+ messageMetadata: { finishReason }
962
+ };
963
+ }
964
+ const chunkBody = JSON.stringify(eventToSend);
965
+ this._storeStreamChunk(streamId, chunkBody);
966
+ this._broadcastChatMessage({
967
+ body: chunkBody,
968
+ done: false,
969
+ id,
970
+ type: MessageType.CF_AGENT_USE_CHAT_RESPONSE,
971
+ ...continuation && { continuation: true }
972
+ });
973
+ } catch (_error) {}
974
+ }
975
+ }
976
+ async _sendPlaintextReply(id, streamId, reader, message, streamCompleted, continuation = false) {
977
+ this._broadcastTextEvent(streamId, {
978
+ type: "text-start",
979
+ id
980
+ }, continuation);
981
+ while (true) {
982
+ const { done, value } = await reader.read();
983
+ if (done) {
984
+ this._broadcastTextEvent(streamId, {
985
+ type: "text-end",
986
+ id
987
+ }, continuation);
988
+ this._completeStream(streamId);
989
+ streamCompleted.value = true;
990
+ this._broadcastChatMessage({
991
+ body: "",
992
+ done: true,
993
+ id,
994
+ type: MessageType.CF_AGENT_USE_CHAT_RESPONSE,
995
+ ...continuation && { continuation: true }
996
+ });
997
+ break;
998
+ }
999
+ const chunk = decoder.decode(value);
1000
+ if (chunk.length > 0) {
1001
+ message.parts.push({
1002
+ type: "text",
1003
+ text: chunk
1004
+ });
1005
+ this._broadcastTextEvent(streamId, {
1006
+ type: "text-delta",
1007
+ id,
1008
+ delta: chunk
1009
+ }, continuation);
1010
+ }
1011
+ }
1012
+ }
1013
+ updateDynamicToolPart(message, options) {
1014
+ const part = message.parts.find((part) => part.type === "dynamic-tool" && part.toolCallId === options.toolCallId);
1015
+ const anyOptions = options;
1016
+ const anyPart = part;
1017
+ if (part != null) {
1018
+ part.state = options.state;
1019
+ anyPart.toolName = options.toolName;
1020
+ anyPart.input = anyOptions.input;
1021
+ anyPart.output = anyOptions.output;
1022
+ anyPart.errorText = anyOptions.errorText;
1023
+ anyPart.rawInput = anyOptions.rawInput ?? anyPart.rawInput;
1024
+ anyPart.preliminary = anyOptions.preliminary;
1025
+ if (anyOptions.providerMetadata != null && part.state === "input-available") part.callProviderMetadata = anyOptions.providerMetadata;
1026
+ } else message.parts.push({
1027
+ type: "dynamic-tool",
1028
+ toolName: options.toolName,
1029
+ toolCallId: options.toolCallId,
1030
+ state: options.state,
1031
+ input: anyOptions.input,
1032
+ output: anyOptions.output,
1033
+ errorText: anyOptions.errorText,
1034
+ preliminary: anyOptions.preliminary,
1035
+ ...anyOptions.providerMetadata != null ? { callProviderMetadata: anyOptions.providerMetadata } : {}
1036
+ });
1037
+ }
1038
+ async updateToolPart(message, options) {
1039
+ const { isToolUIPart } = await import("ai");
1040
+ const part = message.parts.find((part) => isToolUIPart(part) && part.toolCallId === options.toolCallId);
1041
+ const anyOptions = options;
1042
+ const anyPart = part;
1043
+ if (part != null) {
1044
+ part.state = options.state;
1045
+ anyPart.input = anyOptions.input;
1046
+ anyPart.output = anyOptions.output;
1047
+ anyPart.errorText = anyOptions.errorText;
1048
+ anyPart.rawInput = anyOptions.rawInput;
1049
+ anyPart.preliminary = anyOptions.preliminary;
1050
+ anyPart.providerExecuted = anyOptions.providerExecuted ?? part.providerExecuted;
1051
+ if (anyOptions.providerMetadata != null && part.state === "input-available") part.callProviderMetadata = anyOptions.providerMetadata;
1052
+ } else message.parts.push({
1053
+ type: `tool-${options.toolName}`,
1054
+ toolCallId: options.toolCallId,
1055
+ state: options.state,
1056
+ input: anyOptions.input,
1057
+ output: anyOptions.output,
1058
+ rawInput: anyOptions.rawInput,
1059
+ errorText: anyOptions.errorText,
1060
+ providerExecuted: anyOptions.providerExecuted,
1061
+ preliminary: anyOptions.preliminary,
1062
+ ...anyOptions.providerMetadata != null ? { callProviderMetadata: anyOptions.providerMetadata } : {}
1063
+ });
1064
+ }
1065
+ async updateMessageMetadata(message, metadata) {
1066
+ if (metadata != null) message.metadata = message.metadata != null ? {
1067
+ ...message.metadata,
1068
+ ...metadata
1069
+ } : metadata;
1070
+ }
692
1071
  async _reply(id, response, excludeBroadcastIds = [], options = {}) {
693
1072
  const { continuation = false } = options;
694
1073
  return this._tryCatchChat(async () => {
@@ -703,7 +1082,6 @@ var AIChatAgent = class extends Agent {
703
1082
  return;
704
1083
  }
705
1084
  const streamId = this._startStream(id);
706
- const { getToolName: getToolName$1, isToolUIPart: isToolUIPart$1, parsePartialJson } = await import("ai");
707
1085
  const reader = response.body.getReader();
708
1086
  const message = {
709
1087
  id: `assistant_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
@@ -714,358 +1092,13 @@ var AIChatAgent = class extends Agent {
714
1092
  this._streamCompletionPromise = new Promise((resolve) => {
715
1093
  this._streamCompletionResolve = resolve;
716
1094
  });
717
- let activeTextParts = {};
718
- let activeReasoningParts = {};
719
- const partialToolCalls = {};
720
- function updateDynamicToolPart(options$1) {
721
- const part = message.parts.find((part$1) => part$1.type === "dynamic-tool" && part$1.toolCallId === options$1.toolCallId);
722
- const anyOptions = options$1;
723
- const anyPart = part;
724
- if (part != null) {
725
- part.state = options$1.state;
726
- anyPart.toolName = options$1.toolName;
727
- anyPart.input = anyOptions.input;
728
- anyPart.output = anyOptions.output;
729
- anyPart.errorText = anyOptions.errorText;
730
- anyPart.rawInput = anyOptions.rawInput ?? anyPart.rawInput;
731
- anyPart.preliminary = anyOptions.preliminary;
732
- if (anyOptions.providerMetadata != null && part.state === "input-available") part.callProviderMetadata = anyOptions.providerMetadata;
733
- } else message.parts.push({
734
- type: "dynamic-tool",
735
- toolName: options$1.toolName,
736
- toolCallId: options$1.toolCallId,
737
- state: options$1.state,
738
- input: anyOptions.input,
739
- output: anyOptions.output,
740
- errorText: anyOptions.errorText,
741
- preliminary: anyOptions.preliminary,
742
- ...anyOptions.providerMetadata != null ? { callProviderMetadata: anyOptions.providerMetadata } : {}
743
- });
744
- }
745
- function updateToolPart(options$1) {
746
- const part = message.parts.find((part$1) => isToolUIPart$1(part$1) && part$1.toolCallId === options$1.toolCallId);
747
- const anyOptions = options$1;
748
- const anyPart = part;
749
- if (part != null) {
750
- part.state = options$1.state;
751
- anyPart.input = anyOptions.input;
752
- anyPart.output = anyOptions.output;
753
- anyPart.errorText = anyOptions.errorText;
754
- anyPart.rawInput = anyOptions.rawInput;
755
- anyPart.preliminary = anyOptions.preliminary;
756
- anyPart.providerExecuted = anyOptions.providerExecuted ?? part.providerExecuted;
757
- if (anyOptions.providerMetadata != null && part.state === "input-available") part.callProviderMetadata = anyOptions.providerMetadata;
758
- } else message.parts.push({
759
- type: `tool-${options$1.toolName}`,
760
- toolCallId: options$1.toolCallId,
761
- state: options$1.state,
762
- input: anyOptions.input,
763
- output: anyOptions.output,
764
- rawInput: anyOptions.rawInput,
765
- errorText: anyOptions.errorText,
766
- providerExecuted: anyOptions.providerExecuted,
767
- preliminary: anyOptions.preliminary,
768
- ...anyOptions.providerMetadata != null ? { callProviderMetadata: anyOptions.providerMetadata } : {}
769
- });
770
- }
771
- async function updateMessageMetadata(metadata) {
772
- if (metadata != null) message.metadata = message.metadata != null ? {
773
- ...message.metadata,
774
- ...metadata
775
- } : metadata;
776
- }
777
1095
  const isSSE = (response.headers.get("content-type") || "").includes("text/event-stream");
778
- if (!isSSE) this._broadcastTextEvent(streamId, {
779
- type: "text-start",
780
- id
781
- }, continuation);
782
- let streamCompleted = false;
1096
+ const streamCompleted = { value: false };
783
1097
  try {
784
- while (true) {
785
- const { done, value } = await reader.read();
786
- if (done) {
787
- if (!isSSE) this._broadcastTextEvent(streamId, {
788
- type: "text-end",
789
- id
790
- }, continuation);
791
- this._completeStream(streamId);
792
- streamCompleted = true;
793
- this._broadcastChatMessage({
794
- body: "",
795
- done: true,
796
- id,
797
- type: MessageType.CF_AGENT_USE_CHAT_RESPONSE,
798
- ...continuation && { continuation: true }
799
- });
800
- break;
801
- }
802
- const chunk = decoder.decode(value);
803
- if (isSSE) {
804
- const lines = chunk.split("\n");
805
- for (const line of lines) if (line.startsWith("data: ") && line !== "data: [DONE]") try {
806
- const data = JSON.parse(line.slice(6));
807
- switch (data.type) {
808
- case "text-start": {
809
- const textPart = {
810
- type: "text",
811
- text: "",
812
- providerMetadata: data.providerMetadata,
813
- state: "streaming"
814
- };
815
- activeTextParts[data.id] = textPart;
816
- message.parts.push(textPart);
817
- break;
818
- }
819
- case "text-delta": {
820
- const textPart = activeTextParts[data.id];
821
- textPart.text += data.delta;
822
- textPart.providerMetadata = data.providerMetadata ?? textPart.providerMetadata;
823
- break;
824
- }
825
- case "text-end": {
826
- const textPart = activeTextParts[data.id];
827
- textPart.state = "done";
828
- textPart.providerMetadata = data.providerMetadata ?? textPart.providerMetadata;
829
- delete activeTextParts[data.id];
830
- break;
831
- }
832
- case "reasoning-start": {
833
- const reasoningPart = {
834
- type: "reasoning",
835
- text: "",
836
- providerMetadata: data.providerMetadata,
837
- state: "streaming"
838
- };
839
- activeReasoningParts[data.id] = reasoningPart;
840
- message.parts.push(reasoningPart);
841
- break;
842
- }
843
- case "reasoning-delta": {
844
- const reasoningPart = activeReasoningParts[data.id];
845
- reasoningPart.text += data.delta;
846
- reasoningPart.providerMetadata = data.providerMetadata ?? reasoningPart.providerMetadata;
847
- break;
848
- }
849
- case "reasoning-end": {
850
- const reasoningPart = activeReasoningParts[data.id];
851
- reasoningPart.providerMetadata = data.providerMetadata ?? reasoningPart.providerMetadata;
852
- reasoningPart.state = "done";
853
- delete activeReasoningParts[data.id];
854
- break;
855
- }
856
- case "file":
857
- message.parts.push({
858
- type: "file",
859
- mediaType: data.mediaType,
860
- url: data.url
861
- });
862
- break;
863
- case "source-url":
864
- message.parts.push({
865
- type: "source-url",
866
- sourceId: data.sourceId,
867
- url: data.url,
868
- title: data.title,
869
- providerMetadata: data.providerMetadata
870
- });
871
- break;
872
- case "source-document":
873
- message.parts.push({
874
- type: "source-document",
875
- sourceId: data.sourceId,
876
- mediaType: data.mediaType,
877
- title: data.title,
878
- filename: data.filename,
879
- providerMetadata: data.providerMetadata
880
- });
881
- break;
882
- case "tool-input-start": {
883
- const toolInvocations = message.parts.filter(isToolUIPart$1);
884
- partialToolCalls[data.toolCallId] = {
885
- text: "",
886
- toolName: data.toolName,
887
- index: toolInvocations.length,
888
- dynamic: data.dynamic
889
- };
890
- if (data.dynamic) updateDynamicToolPart({
891
- toolCallId: data.toolCallId,
892
- toolName: data.toolName,
893
- state: "input-streaming",
894
- input: void 0
895
- });
896
- else updateToolPart({
897
- toolCallId: data.toolCallId,
898
- toolName: data.toolName,
899
- state: "input-streaming",
900
- input: void 0
901
- });
902
- break;
903
- }
904
- case "tool-input-delta": {
905
- const partialToolCall = partialToolCalls[data.toolCallId];
906
- partialToolCall.text += data.inputTextDelta;
907
- const partialArgs = (await parsePartialJson(partialToolCall.text)).value;
908
- if (partialToolCall.dynamic) updateDynamicToolPart({
909
- toolCallId: data.toolCallId,
910
- toolName: partialToolCall.toolName,
911
- state: "input-streaming",
912
- input: partialArgs
913
- });
914
- else updateToolPart({
915
- toolCallId: data.toolCallId,
916
- toolName: partialToolCall.toolName,
917
- state: "input-streaming",
918
- input: partialArgs
919
- });
920
- break;
921
- }
922
- case "tool-input-available":
923
- if (data.dynamic) updateDynamicToolPart({
924
- toolCallId: data.toolCallId,
925
- toolName: data.toolName,
926
- state: "input-available",
927
- input: data.input,
928
- providerMetadata: data.providerMetadata
929
- });
930
- else updateToolPart({
931
- toolCallId: data.toolCallId,
932
- toolName: data.toolName,
933
- state: "input-available",
934
- input: data.input,
935
- providerExecuted: data.providerExecuted,
936
- providerMetadata: data.providerMetadata
937
- });
938
- break;
939
- case "tool-input-error":
940
- if (data.dynamic) updateDynamicToolPart({
941
- toolCallId: data.toolCallId,
942
- toolName: data.toolName,
943
- state: "output-error",
944
- input: data.input,
945
- errorText: data.errorText,
946
- providerMetadata: data.providerMetadata
947
- });
948
- else updateToolPart({
949
- toolCallId: data.toolCallId,
950
- toolName: data.toolName,
951
- state: "output-error",
952
- input: void 0,
953
- rawInput: data.input,
954
- errorText: data.errorText,
955
- providerExecuted: data.providerExecuted,
956
- providerMetadata: data.providerMetadata
957
- });
958
- break;
959
- case "tool-output-available":
960
- if (data.dynamic) {
961
- const toolInvocation = message.parts.filter((part) => part.type === "dynamic-tool").find((invocation) => invocation.toolCallId === data.toolCallId);
962
- if (!toolInvocation) throw new Error("Tool invocation not found");
963
- updateDynamicToolPart({
964
- toolCallId: data.toolCallId,
965
- toolName: toolInvocation.toolName,
966
- state: "output-available",
967
- input: toolInvocation.input,
968
- output: data.output,
969
- preliminary: data.preliminary
970
- });
971
- } else {
972
- const toolInvocation = message.parts.filter(isToolUIPart$1).find((invocation) => invocation.toolCallId === data.toolCallId);
973
- if (!toolInvocation) throw new Error("Tool invocation not found");
974
- updateToolPart({
975
- toolCallId: data.toolCallId,
976
- toolName: getToolName$1(toolInvocation),
977
- state: "output-available",
978
- input: toolInvocation.input,
979
- output: data.output,
980
- providerExecuted: data.providerExecuted,
981
- preliminary: data.preliminary
982
- });
983
- }
984
- break;
985
- case "tool-output-error":
986
- if (data.dynamic) {
987
- const toolInvocation = message.parts.filter((part) => part.type === "dynamic-tool").find((invocation) => invocation.toolCallId === data.toolCallId);
988
- if (!toolInvocation) throw new Error("Tool invocation not found");
989
- updateDynamicToolPart({
990
- toolCallId: data.toolCallId,
991
- toolName: toolInvocation.toolName,
992
- state: "output-error",
993
- input: toolInvocation.input,
994
- errorText: data.errorText
995
- });
996
- } else {
997
- const toolInvocation = message.parts.filter(isToolUIPart$1).find((invocation) => invocation.toolCallId === data.toolCallId);
998
- if (!toolInvocation) throw new Error("Tool invocation not found");
999
- updateToolPart({
1000
- toolCallId: data.toolCallId,
1001
- toolName: getToolName$1(toolInvocation),
1002
- state: "output-error",
1003
- input: toolInvocation.input,
1004
- rawInput: "rawInput" in toolInvocation ? toolInvocation.rawInput : void 0,
1005
- errorText: data.errorText
1006
- });
1007
- }
1008
- break;
1009
- case "start-step":
1010
- message.parts.push({ type: "step-start" });
1011
- break;
1012
- case "finish-step":
1013
- activeTextParts = {};
1014
- activeReasoningParts = {};
1015
- break;
1016
- case "start":
1017
- if (data.messageId != null) message.id = data.messageId;
1018
- await updateMessageMetadata(data.messageMetadata);
1019
- break;
1020
- case "finish":
1021
- await updateMessageMetadata(data.messageMetadata);
1022
- break;
1023
- case "message-metadata":
1024
- await updateMessageMetadata(data.messageMetadata);
1025
- break;
1026
- case "error":
1027
- this._broadcastChatMessage({
1028
- error: true,
1029
- body: data.errorText ?? JSON.stringify(data),
1030
- done: false,
1031
- id,
1032
- type: MessageType.CF_AGENT_USE_CHAT_RESPONSE
1033
- });
1034
- break;
1035
- }
1036
- let eventToSend = data;
1037
- if (data.type === "finish" && "finishReason" in data) {
1038
- const { finishReason, ...rest } = data;
1039
- eventToSend = {
1040
- ...rest,
1041
- type: "finish",
1042
- messageMetadata: { finishReason }
1043
- };
1044
- }
1045
- const chunkBody = JSON.stringify(eventToSend);
1046
- this._storeStreamChunk(streamId, chunkBody);
1047
- this._broadcastChatMessage({
1048
- body: chunkBody,
1049
- done: false,
1050
- id,
1051
- type: MessageType.CF_AGENT_USE_CHAT_RESPONSE,
1052
- ...continuation && { continuation: true }
1053
- });
1054
- } catch (_error) {}
1055
- } else if (chunk.length > 0) {
1056
- message.parts.push({
1057
- type: "text",
1058
- text: chunk
1059
- });
1060
- this._broadcastTextEvent(streamId, {
1061
- type: "text-delta",
1062
- id,
1063
- delta: chunk
1064
- }, continuation);
1065
- }
1066
- }
1098
+ if (isSSE) await this._streamSSEReply(id, streamId, reader, message, streamCompleted, continuation);
1099
+ else await this._sendPlaintextReply(id, streamId, reader, message, streamCompleted, continuation);
1067
1100
  } catch (error) {
1068
- if (!streamCompleted) {
1101
+ if (!streamCompleted.value) {
1069
1102
  this._markStreamError(streamId);
1070
1103
  this._broadcastChatMessage({
1071
1104
  body: error instanceof Error ? error.message : "Stream error",