@iota-uz/sdk 0.4.17 → 0.4.19
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/bichat/index.cjs +491 -67
- package/dist/bichat/index.cjs.map +1 -1
- package/dist/bichat/index.d.cts +15 -6
- package/dist/bichat/index.d.ts +15 -6
- package/dist/bichat/index.mjs +493 -69
- package/dist/bichat/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tailwind/compiled.css +1 -1
package/dist/bichat/index.cjs
CHANGED
|
@@ -1518,6 +1518,10 @@ var ChatMachine = class {
|
|
|
1518
1518
|
}
|
|
1519
1519
|
/** Sets turns from fetch, preserving pending user-only turns if server hasn't caught up. */
|
|
1520
1520
|
_setTurnsFromFetch(fetchedTurns) {
|
|
1521
|
+
if (!Array.isArray(fetchedTurns)) {
|
|
1522
|
+
console.warn("[ChatMachine] Ignoring malformed turns payload from fetchSession");
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1521
1525
|
const prev = this.state.messaging.turns;
|
|
1522
1526
|
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1523
1527
|
if (hasPendingUserOnly && (!fetchedTurns || fetchedTurns.length === 0)) {
|
|
@@ -1907,6 +1911,7 @@ var ChatMachine = class {
|
|
|
1907
1911
|
const curSessionId = this.state.session.currentSessionId;
|
|
1908
1912
|
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1909
1913
|
if (!curSessionId || !curPendingQuestion) return;
|
|
1914
|
+
const previousTurns = this.state.messaging.turns;
|
|
1910
1915
|
this._updateMessaging({ loading: true });
|
|
1911
1916
|
this._updateSession({ error: null, errorRetryable: false });
|
|
1912
1917
|
const previousPendingQuestion = curPendingQuestion;
|
|
@@ -1924,19 +1929,28 @@ var ChatMachine = class {
|
|
|
1924
1929
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1925
1930
|
if (this.disposed) return;
|
|
1926
1931
|
if (fetchResult) {
|
|
1927
|
-
this.
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1932
|
+
this._updateSession({ session: fetchResult.session });
|
|
1933
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1934
|
+
const hasMalformedRefresh = previousTurns.length > 0 && Array.isArray(fetchResult.turns) && fetchResult.turns.length === 0;
|
|
1935
|
+
if (hasMalformedRefresh) {
|
|
1936
|
+
console.warn("[ChatMachine] Preserving previous turns due to empty post-HITL refetch payload", {
|
|
1937
|
+
sessionId: curSessionId,
|
|
1938
|
+
previousTurnCount: previousTurns.length
|
|
1939
|
+
});
|
|
1940
|
+
this._updateSession({
|
|
1941
|
+
error: "Failed to fully refresh session. Showing last known messages.",
|
|
1942
|
+
errorRetryable: true
|
|
1943
|
+
});
|
|
1944
|
+
} else {
|
|
1945
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1946
|
+
}
|
|
1931
1947
|
} else {
|
|
1932
|
-
this.
|
|
1933
|
-
this._updateSession({ error: "Failed to load updated session", errorRetryable: false });
|
|
1948
|
+
this._updateSession({ error: "Failed to load updated session", errorRetryable: true });
|
|
1934
1949
|
}
|
|
1935
1950
|
} catch (fetchErr) {
|
|
1936
1951
|
if (this.disposed) return;
|
|
1937
|
-
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1938
1952
|
const normalized = normalizeRPCError(fetchErr, "Failed to load updated session");
|
|
1939
|
-
this._updateSession({ error: normalized.userMessage, errorRetryable:
|
|
1953
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: true });
|
|
1940
1954
|
}
|
|
1941
1955
|
}
|
|
1942
1956
|
} else {
|
|
@@ -3296,7 +3310,7 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3296
3310
|
const [currentStep, setCurrentStep] = React.useState(0);
|
|
3297
3311
|
const [answers, setAnswers] = React.useState({});
|
|
3298
3312
|
const [otherTexts, setOtherTexts] = React.useState({});
|
|
3299
|
-
const questions = pendingQuestion.questions;
|
|
3313
|
+
const questions = Array.isArray(pendingQuestion.questions) ? pendingQuestion.questions : [];
|
|
3300
3314
|
const currentQuestion = questions[currentStep];
|
|
3301
3315
|
const isLastStep = currentStep === questions.length - 1;
|
|
3302
3316
|
const isFirstStep = currentStep === 0;
|
|
@@ -3390,9 +3404,31 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3390
3404
|
e.preventDefault();
|
|
3391
3405
|
handleNext();
|
|
3392
3406
|
};
|
|
3393
|
-
if (!currentQuestion)
|
|
3407
|
+
if (!currentQuestion) {
|
|
3408
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-slide-up rounded-2xl border border-amber-200 dark:border-amber-700/50 bg-gradient-to-b from-amber-50/70 to-white dark:from-amber-950/20 dark:to-gray-900/80 shadow-sm overflow-hidden p-4", children: [
|
|
3409
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-800 dark:text-gray-200", children: t("BiChat.Error.SomethingWentWrong") }),
|
|
3410
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Error.UnexpectedError") }),
|
|
3411
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3412
|
+
"button",
|
|
3413
|
+
{
|
|
3414
|
+
type: "button",
|
|
3415
|
+
onClick: handleRejectPendingQuestion,
|
|
3416
|
+
disabled: loading,
|
|
3417
|
+
className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-40",
|
|
3418
|
+
children: [
|
|
3419
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 14, weight: "bold" }),
|
|
3420
|
+
t("BiChat.InlineQuestion.Dismiss")
|
|
3421
|
+
]
|
|
3422
|
+
}
|
|
3423
|
+
) })
|
|
3424
|
+
] });
|
|
3425
|
+
}
|
|
3394
3426
|
const isMultiSelect = currentQuestion.type === "MULTIPLE_CHOICE";
|
|
3395
|
-
const options = currentQuestion.options || []
|
|
3427
|
+
const options = (currentQuestion.options || []).filter((option) => Boolean(option && typeof option.label === "string")).map((option, index) => ({
|
|
3428
|
+
id: option.id || `${currentQuestion.id}-option-${index}`,
|
|
3429
|
+
label: option.label,
|
|
3430
|
+
value: option.value || option.label
|
|
3431
|
+
}));
|
|
3396
3432
|
const isOtherSelected = currentAnswer?.customText !== void 0;
|
|
3397
3433
|
const canProceed = isCurrentAnswerValid();
|
|
3398
3434
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-slide-up rounded-2xl border border-gray-200 dark:border-gray-700/50 bg-gradient-to-b from-primary-50/80 to-white dark:from-primary-950/30 dark:to-gray-900/80 shadow-sm overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
|
|
@@ -4306,7 +4342,8 @@ function TurnBubble({
|
|
|
4306
4342
|
userTurn: classNames?.userTurn ?? defaultClassNames3.userTurn,
|
|
4307
4343
|
assistantTurn: classNames?.assistantTurn ?? defaultClassNames3.assistantTurn
|
|
4308
4344
|
};
|
|
4309
|
-
const
|
|
4345
|
+
const userContent = typeof turn.userTurn?.content === "string" ? turn.userTurn.content : "";
|
|
4346
|
+
const isSystemSummaryTurn = userContent.trim() === "" && turn.assistantTurn?.role === "system";
|
|
4310
4347
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classes.root, "data-turn-id": turn.id, children: [
|
|
4311
4348
|
!isSystemSummaryTurn && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.userTurn, children: renderUserTurn ? renderUserTurn(turn) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
4312
4349
|
UserTurnView,
|
|
@@ -8084,12 +8121,63 @@ function DefaultErrorContent({
|
|
|
8084
8121
|
] })
|
|
8085
8122
|
] });
|
|
8086
8123
|
}
|
|
8124
|
+
function StaticEmergencyErrorContent({
|
|
8125
|
+
error,
|
|
8126
|
+
onReset
|
|
8127
|
+
}) {
|
|
8128
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center justify-center p-8 text-center min-h-[200px]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col items-center", children: [
|
|
8129
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mb-5", children: [
|
|
8130
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full bg-red-100 scale-150 blur-md" }),
|
|
8131
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex items-center justify-center w-14 h-14 rounded-full bg-red-50 border border-red-200/60", children: /* @__PURE__ */ jsxRuntime.jsx(react.WarningCircle, { size: 28, className: "text-red-500", weight: "fill" }) })
|
|
8132
|
+
] }),
|
|
8133
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-1.5", children: "Something went wrong" }),
|
|
8134
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mb-5 max-w-md leading-relaxed", children: error?.message || "An unexpected UI error occurred." }),
|
|
8135
|
+
onReset && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8136
|
+
"button",
|
|
8137
|
+
{
|
|
8138
|
+
type: "button",
|
|
8139
|
+
onClick: onReset,
|
|
8140
|
+
className: "flex items-center gap-2 px-5 py-2.5 bg-red-600 hover:bg-red-700 active:bg-red-800 text-white rounded-lg transition-colors shadow-sm text-sm font-medium",
|
|
8141
|
+
children: [
|
|
8142
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { size: 16, weight: "bold" }),
|
|
8143
|
+
"Try again"
|
|
8144
|
+
]
|
|
8145
|
+
}
|
|
8146
|
+
)
|
|
8147
|
+
] }) });
|
|
8148
|
+
}
|
|
8149
|
+
var FallbackGuard = class extends React.Component {
|
|
8150
|
+
constructor(props) {
|
|
8151
|
+
super(props);
|
|
8152
|
+
this.state = { fallbackFailed: false };
|
|
8153
|
+
}
|
|
8154
|
+
static getDerivedStateFromError() {
|
|
8155
|
+
return { fallbackFailed: true };
|
|
8156
|
+
}
|
|
8157
|
+
componentDidCatch(error, errorInfo) {
|
|
8158
|
+
this.props.onFallbackError?.(error, errorInfo);
|
|
8159
|
+
}
|
|
8160
|
+
render() {
|
|
8161
|
+
if (this.state.fallbackFailed) {
|
|
8162
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StaticEmergencyErrorContent, { error: this.props.primaryError, onReset: this.props.onReset });
|
|
8163
|
+
}
|
|
8164
|
+
return this.props.renderFallback();
|
|
8165
|
+
}
|
|
8166
|
+
};
|
|
8087
8167
|
var ErrorBoundary = class extends React.Component {
|
|
8088
8168
|
constructor(props) {
|
|
8089
8169
|
super(props);
|
|
8090
8170
|
this.handleReset = () => {
|
|
8091
8171
|
this.setState({ hasError: false, error: null });
|
|
8092
8172
|
};
|
|
8173
|
+
this.handleFallbackError = (error, errorInfo) => {
|
|
8174
|
+
console.error("React Error Boundary fallback crashed:", {
|
|
8175
|
+
primaryError: this.state.error,
|
|
8176
|
+
fallbackError: error,
|
|
8177
|
+
errorInfo
|
|
8178
|
+
});
|
|
8179
|
+
this.props.onError?.(error, errorInfo);
|
|
8180
|
+
};
|
|
8093
8181
|
this.state = { hasError: false, error: null };
|
|
8094
8182
|
}
|
|
8095
8183
|
static getDerivedStateFromError(error) {
|
|
@@ -8101,13 +8189,24 @@ var ErrorBoundary = class extends React.Component {
|
|
|
8101
8189
|
}
|
|
8102
8190
|
render() {
|
|
8103
8191
|
if (this.state.hasError) {
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8192
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8193
|
+
FallbackGuard,
|
|
8194
|
+
{
|
|
8195
|
+
primaryError: this.state.error,
|
|
8196
|
+
onReset: this.handleReset,
|
|
8197
|
+
onFallbackError: this.handleFallbackError,
|
|
8198
|
+
renderFallback: () => {
|
|
8199
|
+
if (this.props.fallback) {
|
|
8200
|
+
if (typeof this.props.fallback === "function") {
|
|
8201
|
+
return this.props.fallback(this.state.error, this.handleReset);
|
|
8202
|
+
}
|
|
8203
|
+
return this.props.fallback;
|
|
8204
|
+
}
|
|
8205
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DefaultErrorContent, { error: this.state.error, onReset: this.handleReset });
|
|
8206
|
+
}
|
|
8207
|
+
},
|
|
8208
|
+
`${this.state.error?.name ?? "Error"}:${this.state.error?.message ?? ""}`
|
|
8209
|
+
);
|
|
8111
8210
|
}
|
|
8112
8211
|
return this.props.children;
|
|
8113
8212
|
}
|
|
@@ -12066,6 +12165,7 @@ function toSessionArtifact(artifact) {
|
|
|
12066
12165
|
id: artifact.id,
|
|
12067
12166
|
sessionId: artifact.sessionId,
|
|
12068
12167
|
messageId: artifact.messageId,
|
|
12168
|
+
uploadId: artifact.uploadId,
|
|
12069
12169
|
type: artifact.type,
|
|
12070
12170
|
name: artifact.name,
|
|
12071
12171
|
description: artifact.description,
|
|
@@ -12076,21 +12176,272 @@ function toSessionArtifact(artifact) {
|
|
|
12076
12176
|
createdAt: artifact.createdAt
|
|
12077
12177
|
};
|
|
12078
12178
|
}
|
|
12079
|
-
function
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12179
|
+
function warnMalformedSessionPayload(message, details) {
|
|
12180
|
+
console.warn(`[BiChat] ${message}`, details || {});
|
|
12181
|
+
}
|
|
12182
|
+
function readString(value, fallback = "") {
|
|
12183
|
+
return typeof value === "string" ? value : fallback;
|
|
12184
|
+
}
|
|
12185
|
+
function readNonEmptyString(value) {
|
|
12186
|
+
if (typeof value !== "string") return null;
|
|
12187
|
+
const trimmed = value.trim();
|
|
12188
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12189
|
+
}
|
|
12190
|
+
function readFiniteNumber(value, fallback = 0) {
|
|
12191
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
12192
|
+
}
|
|
12193
|
+
function readOptionalFiniteNumber(value) {
|
|
12194
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
12195
|
+
}
|
|
12196
|
+
function normalizeQuestionType(rawType) {
|
|
12197
|
+
const normalized = readString(rawType).trim().toUpperCase().replace(/[\s-]+/g, "_");
|
|
12198
|
+
return normalized === "MULTIPLE_CHOICE" ? "MULTIPLE_CHOICE" : "SINGLE_CHOICE";
|
|
12199
|
+
}
|
|
12200
|
+
function normalizeMessageRole(rawRole) {
|
|
12201
|
+
const normalized = readString(rawRole).trim().toLowerCase();
|
|
12202
|
+
if (normalized === "user" /* User */) return "user" /* User */;
|
|
12203
|
+
if (normalized === "system" /* System */) return "system" /* System */;
|
|
12204
|
+
if (normalized === "tool" /* Tool */) return "tool" /* Tool */;
|
|
12205
|
+
return "assistant" /* Assistant */;
|
|
12206
|
+
}
|
|
12207
|
+
function sanitizeAttachment(rawAttachment, turnId, index) {
|
|
12208
|
+
if (!isRecord(rawAttachment)) {
|
|
12209
|
+
warnMalformedSessionPayload("Dropped malformed attachment entry", { turnId, index });
|
|
12210
|
+
return null;
|
|
12211
|
+
}
|
|
12212
|
+
const filename = readString(rawAttachment.filename, "attachment");
|
|
12213
|
+
const mimeType = readString(rawAttachment.mimeType, "application/octet-stream");
|
|
12214
|
+
const id = readNonEmptyString(rawAttachment.id) || void 0;
|
|
12215
|
+
const clientKey = readNonEmptyString(rawAttachment.clientKey) || id || `${turnId}-attachment-${index}`;
|
|
12216
|
+
return {
|
|
12217
|
+
id,
|
|
12218
|
+
clientKey,
|
|
12219
|
+
filename,
|
|
12220
|
+
mimeType,
|
|
12221
|
+
sizeBytes: readFiniteNumber(rawAttachment.sizeBytes),
|
|
12222
|
+
uploadId: readOptionalFiniteNumber(rawAttachment.uploadId),
|
|
12223
|
+
base64Data: readNonEmptyString(rawAttachment.base64Data) || void 0,
|
|
12224
|
+
url: readNonEmptyString(rawAttachment.url) || void 0,
|
|
12225
|
+
preview: readNonEmptyString(rawAttachment.preview) || void 0
|
|
12226
|
+
};
|
|
12227
|
+
}
|
|
12228
|
+
function sanitizeUserAttachments(rawAttachments, turnId) {
|
|
12229
|
+
if (!Array.isArray(rawAttachments)) return [];
|
|
12230
|
+
const result = [];
|
|
12231
|
+
for (let i = 0; i < rawAttachments.length; i++) {
|
|
12232
|
+
const sanitized = sanitizeAttachment(rawAttachments[i], turnId, i);
|
|
12233
|
+
if (sanitized) result.push(sanitized);
|
|
12234
|
+
}
|
|
12235
|
+
return result;
|
|
12236
|
+
}
|
|
12237
|
+
function sanitizeAssistantArtifacts(rawArtifacts, turnId) {
|
|
12238
|
+
if (!Array.isArray(rawArtifacts)) return [];
|
|
12239
|
+
const artifacts = [];
|
|
12240
|
+
for (let i = 0; i < rawArtifacts.length; i++) {
|
|
12241
|
+
const raw = rawArtifacts[i];
|
|
12242
|
+
if (!isRecord(raw)) {
|
|
12243
|
+
warnMalformedSessionPayload("Dropped malformed assistant artifact", { turnId, index: i });
|
|
12244
|
+
continue;
|
|
12245
|
+
}
|
|
12246
|
+
const type = readString(raw.type).toLowerCase();
|
|
12247
|
+
if (type !== "excel" && type !== "pdf") {
|
|
12248
|
+
continue;
|
|
12249
|
+
}
|
|
12250
|
+
const url = readNonEmptyString(raw.url);
|
|
12251
|
+
if (!url) {
|
|
12252
|
+
warnMalformedSessionPayload("Dropped assistant artifact without url", { turnId, index: i });
|
|
12253
|
+
continue;
|
|
12254
|
+
}
|
|
12255
|
+
artifacts.push({
|
|
12256
|
+
type,
|
|
12257
|
+
filename: readString(raw.filename, "download"),
|
|
12258
|
+
url,
|
|
12259
|
+
sizeReadable: readNonEmptyString(raw.sizeReadable) || void 0,
|
|
12260
|
+
rowCount: typeof raw.rowCount === "number" && Number.isFinite(raw.rowCount) ? raw.rowCount : void 0,
|
|
12261
|
+
description: readNonEmptyString(raw.description) || void 0
|
|
12262
|
+
});
|
|
12263
|
+
}
|
|
12264
|
+
return artifacts;
|
|
12265
|
+
}
|
|
12266
|
+
function sanitizeAssistantTurn(rawAssistantTurn, fallbackCreatedAt, turnId) {
|
|
12267
|
+
if (rawAssistantTurn == null) return void 0;
|
|
12268
|
+
if (!isRecord(rawAssistantTurn)) {
|
|
12269
|
+
warnMalformedSessionPayload("Dropped malformed assistant turn payload", { turnId });
|
|
12270
|
+
return void 0;
|
|
12271
|
+
}
|
|
12272
|
+
const assistantID = readNonEmptyString(rawAssistantTurn.id);
|
|
12273
|
+
if (!assistantID) {
|
|
12274
|
+
warnMalformedSessionPayload("Dropped assistant turn without id", { turnId });
|
|
12275
|
+
return void 0;
|
|
12276
|
+
}
|
|
12277
|
+
const citations = Array.isArray(rawAssistantTurn.citations) ? rawAssistantTurn.citations.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12278
|
+
id: readString(item.id, `${assistantID}-citation-${index}`),
|
|
12279
|
+
type: readString(item.type),
|
|
12280
|
+
title: readString(item.title),
|
|
12281
|
+
url: readString(item.url),
|
|
12282
|
+
startIndex: readFiniteNumber(item.startIndex),
|
|
12283
|
+
endIndex: readFiniteNumber(item.endIndex),
|
|
12284
|
+
excerpt: readNonEmptyString(item.excerpt) || void 0
|
|
12285
|
+
})) : [];
|
|
12286
|
+
const toolCalls = Array.isArray(rawAssistantTurn.toolCalls) ? rawAssistantTurn.toolCalls.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12287
|
+
id: readString(item.id, `${assistantID}-tool-${index}`),
|
|
12288
|
+
name: readString(item.name),
|
|
12289
|
+
arguments: readString(item.arguments),
|
|
12290
|
+
result: readNonEmptyString(item.result) || void 0,
|
|
12291
|
+
error: readNonEmptyString(item.error) || void 0,
|
|
12292
|
+
durationMs: readFiniteNumber(item.durationMs)
|
|
12293
|
+
})) : [];
|
|
12294
|
+
const codeOutputs = Array.isArray(rawAssistantTurn.codeOutputs) ? rawAssistantTurn.codeOutputs.filter((item) => isRecord(item)).map((item) => ({
|
|
12295
|
+
type: (() => {
|
|
12296
|
+
const normalizedType = readString(item.type, "text").toLowerCase();
|
|
12297
|
+
if (normalizedType === "image" || normalizedType === "error") return normalizedType;
|
|
12298
|
+
return "text";
|
|
12299
|
+
})(),
|
|
12300
|
+
content: readString(item.content),
|
|
12301
|
+
filename: readNonEmptyString(item.filename) || void 0,
|
|
12302
|
+
mimeType: readNonEmptyString(item.mimeType) || void 0,
|
|
12303
|
+
sizeBytes: readOptionalFiniteNumber(item.sizeBytes)
|
|
12304
|
+
})) : [];
|
|
12305
|
+
const debugTrace = isRecord(rawAssistantTurn.debug) ? {
|
|
12306
|
+
generationMs: readOptionalFiniteNumber(rawAssistantTurn.debug.generationMs),
|
|
12307
|
+
usage: isRecord(rawAssistantTurn.debug.usage) ? {
|
|
12308
|
+
promptTokens: readFiniteNumber(rawAssistantTurn.debug.usage.promptTokens),
|
|
12309
|
+
completionTokens: readFiniteNumber(rawAssistantTurn.debug.usage.completionTokens),
|
|
12310
|
+
totalTokens: readFiniteNumber(rawAssistantTurn.debug.usage.totalTokens),
|
|
12311
|
+
cachedTokens: readOptionalFiniteNumber(rawAssistantTurn.debug.usage.cachedTokens),
|
|
12312
|
+
cost: readOptionalFiniteNumber(rawAssistantTurn.debug.usage.cost)
|
|
12313
|
+
} : void 0,
|
|
12314
|
+
tools: Array.isArray(rawAssistantTurn.debug.tools) ? rawAssistantTurn.debug.tools.filter((tool) => isRecord(tool)).map((tool) => ({
|
|
12315
|
+
callId: readNonEmptyString(tool.callId) || void 0,
|
|
12316
|
+
name: readString(tool.name),
|
|
12317
|
+
arguments: readNonEmptyString(tool.arguments) || void 0,
|
|
12318
|
+
result: readNonEmptyString(tool.result) || void 0,
|
|
12319
|
+
error: readNonEmptyString(tool.error) || void 0,
|
|
12320
|
+
durationMs: readOptionalFiniteNumber(tool.durationMs)
|
|
12321
|
+
})) : []
|
|
12322
|
+
} : void 0;
|
|
12323
|
+
return {
|
|
12324
|
+
id: assistantID,
|
|
12325
|
+
role: normalizeMessageRole(rawAssistantTurn.role),
|
|
12326
|
+
content: readString(rawAssistantTurn.content),
|
|
12327
|
+
explanation: readNonEmptyString(rawAssistantTurn.explanation) || void 0,
|
|
12328
|
+
citations,
|
|
12329
|
+
toolCalls,
|
|
12330
|
+
chartData: void 0,
|
|
12331
|
+
artifacts: sanitizeAssistantArtifacts(rawAssistantTurn.artifacts, turnId),
|
|
12332
|
+
codeOutputs,
|
|
12333
|
+
debug: debugTrace,
|
|
12334
|
+
createdAt: readString(rawAssistantTurn.createdAt, fallbackCreatedAt)
|
|
12335
|
+
};
|
|
12336
|
+
}
|
|
12337
|
+
function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
|
|
12338
|
+
if (!isRecord(rawTurn)) {
|
|
12339
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (not an object)", { index });
|
|
12340
|
+
return null;
|
|
12341
|
+
}
|
|
12342
|
+
if (!isRecord(rawTurn.userTurn)) {
|
|
12343
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn)", { index });
|
|
12344
|
+
return null;
|
|
12345
|
+
}
|
|
12346
|
+
const userTurnID = readNonEmptyString(rawTurn.userTurn.id);
|
|
12347
|
+
if (!userTurnID) {
|
|
12348
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn id)", { index });
|
|
12349
|
+
return null;
|
|
12350
|
+
}
|
|
12351
|
+
const turnID = readString(rawTurn.id, userTurnID);
|
|
12352
|
+
const createdAt = readString(
|
|
12353
|
+
rawTurn.createdAt,
|
|
12354
|
+
readString(rawTurn.userTurn.createdAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
12355
|
+
);
|
|
12091
12356
|
return {
|
|
12092
|
-
id:
|
|
12093
|
-
|
|
12357
|
+
id: turnID,
|
|
12358
|
+
sessionId: readString(rawTurn.sessionId, fallbackSessionID),
|
|
12359
|
+
userTurn: {
|
|
12360
|
+
id: userTurnID,
|
|
12361
|
+
content: readString(rawTurn.userTurn.content),
|
|
12362
|
+
attachments: sanitizeUserAttachments(rawTurn.userTurn.attachments, turnID),
|
|
12363
|
+
createdAt: readString(rawTurn.userTurn.createdAt, createdAt)
|
|
12364
|
+
},
|
|
12365
|
+
assistantTurn: sanitizeAssistantTurn(rawTurn.assistantTurn, createdAt, turnID),
|
|
12366
|
+
createdAt
|
|
12367
|
+
};
|
|
12368
|
+
}
|
|
12369
|
+
function sanitizeConversationTurns(rawTurns, sessionID) {
|
|
12370
|
+
if (!Array.isArray(rawTurns)) {
|
|
12371
|
+
warnMalformedSessionPayload("Session payload contained non-array turns field", { sessionID });
|
|
12372
|
+
return [];
|
|
12373
|
+
}
|
|
12374
|
+
const turns = [];
|
|
12375
|
+
let dropped = 0;
|
|
12376
|
+
for (let i = 0; i < rawTurns.length; i++) {
|
|
12377
|
+
const sanitizedTurn = sanitizeConversationTurn(rawTurns[i], i, sessionID);
|
|
12378
|
+
if (sanitizedTurn) {
|
|
12379
|
+
turns.push(sanitizedTurn);
|
|
12380
|
+
} else {
|
|
12381
|
+
dropped++;
|
|
12382
|
+
}
|
|
12383
|
+
}
|
|
12384
|
+
if (dropped > 0) {
|
|
12385
|
+
warnMalformedSessionPayload("Dropped malformed turns from session payload", {
|
|
12386
|
+
sessionID,
|
|
12387
|
+
dropped,
|
|
12388
|
+
total: rawTurns.length
|
|
12389
|
+
});
|
|
12390
|
+
}
|
|
12391
|
+
return turns;
|
|
12392
|
+
}
|
|
12393
|
+
function sanitizePendingQuestion(rawPendingQuestion, sessionID) {
|
|
12394
|
+
if (!rawPendingQuestion) return null;
|
|
12395
|
+
const checkpointID = readNonEmptyString(rawPendingQuestion.checkpointId);
|
|
12396
|
+
if (!checkpointID) {
|
|
12397
|
+
warnMalformedSessionPayload("Dropped malformed pendingQuestion without checkpointId", { sessionID });
|
|
12398
|
+
return null;
|
|
12399
|
+
}
|
|
12400
|
+
if (!Array.isArray(rawPendingQuestion.questions)) {
|
|
12401
|
+
warnMalformedSessionPayload("Pending question had non-array questions payload", {
|
|
12402
|
+
sessionID,
|
|
12403
|
+
checkpointID
|
|
12404
|
+
});
|
|
12405
|
+
}
|
|
12406
|
+
const questions = Array.isArray(rawPendingQuestion.questions) ? rawPendingQuestion.questions.filter((question) => {
|
|
12407
|
+
if (!question || !isRecord(question)) {
|
|
12408
|
+
warnMalformedSessionPayload("Dropped malformed question from pendingQuestion", {
|
|
12409
|
+
sessionID,
|
|
12410
|
+
checkpointID
|
|
12411
|
+
});
|
|
12412
|
+
return false;
|
|
12413
|
+
}
|
|
12414
|
+
return true;
|
|
12415
|
+
}).map((question, index) => {
|
|
12416
|
+
const questionID = readString(question.id, `${checkpointID}-q-${index}`);
|
|
12417
|
+
const options = Array.isArray(question.options) ? question.options.filter((option) => {
|
|
12418
|
+
if (!option || !isRecord(option)) {
|
|
12419
|
+
warnMalformedSessionPayload("Dropped malformed pendingQuestion option", {
|
|
12420
|
+
sessionID,
|
|
12421
|
+
checkpointID,
|
|
12422
|
+
questionID
|
|
12423
|
+
});
|
|
12424
|
+
return false;
|
|
12425
|
+
}
|
|
12426
|
+
return true;
|
|
12427
|
+
}).map((option, optionIndex) => {
|
|
12428
|
+
const label = readString(option.label);
|
|
12429
|
+
return {
|
|
12430
|
+
id: readString(option.id, `${questionID}-opt-${optionIndex}`),
|
|
12431
|
+
label,
|
|
12432
|
+
value: label
|
|
12433
|
+
};
|
|
12434
|
+
}) : [];
|
|
12435
|
+
return {
|
|
12436
|
+
id: questionID,
|
|
12437
|
+
text: readString(question.text),
|
|
12438
|
+
type: normalizeQuestionType(question.type),
|
|
12439
|
+
options
|
|
12440
|
+
};
|
|
12441
|
+
}) : [];
|
|
12442
|
+
return {
|
|
12443
|
+
id: checkpointID,
|
|
12444
|
+
turnId: readString(rawPendingQuestion.turnId),
|
|
12094
12445
|
questions,
|
|
12095
12446
|
status: "PENDING"
|
|
12096
12447
|
};
|
|
@@ -12286,6 +12637,7 @@ var HttpDataSource = class {
|
|
|
12286
12637
|
this.abortController = null;
|
|
12287
12638
|
this.config = {
|
|
12288
12639
|
streamEndpoint: "/stream",
|
|
12640
|
+
uploadEndpoint: "/api/uploads",
|
|
12289
12641
|
timeout: 12e4,
|
|
12290
12642
|
...config
|
|
12291
12643
|
};
|
|
@@ -12321,6 +12673,83 @@ var HttpDataSource = class {
|
|
|
12321
12673
|
}
|
|
12322
12674
|
return headers;
|
|
12323
12675
|
}
|
|
12676
|
+
createUploadHeaders(additionalHeaders) {
|
|
12677
|
+
const headers = new Headers({
|
|
12678
|
+
...this.config.headers,
|
|
12679
|
+
...additionalHeaders
|
|
12680
|
+
});
|
|
12681
|
+
const csrfToken = this.getCSRFToken();
|
|
12682
|
+
if (csrfToken) {
|
|
12683
|
+
headers.set("X-CSRF-Token", csrfToken);
|
|
12684
|
+
}
|
|
12685
|
+
headers.delete("Content-Type");
|
|
12686
|
+
return headers;
|
|
12687
|
+
}
|
|
12688
|
+
async uploadFile(file) {
|
|
12689
|
+
const formData = new FormData();
|
|
12690
|
+
formData.append("file", file);
|
|
12691
|
+
const response = await fetch(`${this.config.baseUrl}${this.config.uploadEndpoint}`, {
|
|
12692
|
+
method: "POST",
|
|
12693
|
+
headers: this.createUploadHeaders(),
|
|
12694
|
+
body: formData
|
|
12695
|
+
});
|
|
12696
|
+
let payload = null;
|
|
12697
|
+
try {
|
|
12698
|
+
payload = await response.json();
|
|
12699
|
+
} catch {
|
|
12700
|
+
payload = null;
|
|
12701
|
+
}
|
|
12702
|
+
if (!response.ok) {
|
|
12703
|
+
const errorMessage = isRecord(payload) && typeof payload.error === "string" ? payload.error : `Upload failed: HTTP ${response.status}`;
|
|
12704
|
+
throw new Error(errorMessage);
|
|
12705
|
+
}
|
|
12706
|
+
if (!isRecord(payload) || typeof payload.id !== "number" || payload.id <= 0) {
|
|
12707
|
+
throw new Error("Upload failed: invalid response payload");
|
|
12708
|
+
}
|
|
12709
|
+
return {
|
|
12710
|
+
id: payload.id,
|
|
12711
|
+
url: typeof payload.url === "string" ? payload.url : "",
|
|
12712
|
+
path: typeof payload.path === "string" ? payload.path : "",
|
|
12713
|
+
name: typeof payload.name === "string" ? payload.name : file.name,
|
|
12714
|
+
mimetype: typeof payload.mimetype === "string" ? payload.mimetype : file.type,
|
|
12715
|
+
size: typeof payload.size === "number" && Number.isFinite(payload.size) ? payload.size : file.size
|
|
12716
|
+
};
|
|
12717
|
+
}
|
|
12718
|
+
async attachmentToFile(attachment) {
|
|
12719
|
+
if (attachment.base64Data && attachment.base64Data.trim().length > 0) {
|
|
12720
|
+
const base64Data = attachment.base64Data.trim();
|
|
12721
|
+
const dataUrl = base64Data.startsWith("data:") ? base64Data : `data:${attachment.mimeType || "application/octet-stream"};base64,${base64Data}`;
|
|
12722
|
+
const blob = await fetch(dataUrl).then((response) => response.blob());
|
|
12723
|
+
return new File([blob], attachment.filename, {
|
|
12724
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
12725
|
+
});
|
|
12726
|
+
}
|
|
12727
|
+
if (attachment.url) {
|
|
12728
|
+
const response = await fetch(attachment.url);
|
|
12729
|
+
if (!response.ok) {
|
|
12730
|
+
throw new Error(`Failed to read attachment source: HTTP ${response.status}`);
|
|
12731
|
+
}
|
|
12732
|
+
const blob = await response.blob();
|
|
12733
|
+
return new File([blob], attachment.filename, {
|
|
12734
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
12735
|
+
});
|
|
12736
|
+
}
|
|
12737
|
+
throw new Error(`Attachment "${attachment.filename}" has no uploadable data`);
|
|
12738
|
+
}
|
|
12739
|
+
async ensureAttachmentUpload(attachment) {
|
|
12740
|
+
if (typeof attachment.uploadId === "number" && attachment.uploadId > 0) {
|
|
12741
|
+
return {
|
|
12742
|
+
id: attachment.uploadId,
|
|
12743
|
+
url: attachment.url || "",
|
|
12744
|
+
path: "",
|
|
12745
|
+
name: attachment.filename,
|
|
12746
|
+
mimetype: attachment.mimeType,
|
|
12747
|
+
size: attachment.sizeBytes
|
|
12748
|
+
};
|
|
12749
|
+
}
|
|
12750
|
+
const file = await this.attachmentToFile(attachment);
|
|
12751
|
+
return this.uploadFile(file);
|
|
12752
|
+
}
|
|
12324
12753
|
async callRPC(method, params) {
|
|
12325
12754
|
return this.rpc.callTyped(method, params);
|
|
12326
12755
|
}
|
|
@@ -12343,11 +12772,22 @@ var HttpDataSource = class {
|
|
|
12343
12772
|
return { artifacts: [], hasMore: false, nextOffset: 0 };
|
|
12344
12773
|
})
|
|
12345
12774
|
]);
|
|
12346
|
-
const
|
|
12775
|
+
const sanitizedTurns = sanitizeConversationTurns(data.turns, id);
|
|
12776
|
+
const turns = attachArtifactsToTurns(
|
|
12777
|
+
normalizeTurns(sanitizedTurns),
|
|
12778
|
+
artifactsData.artifacts || []
|
|
12779
|
+
);
|
|
12780
|
+
const pendingQuestion = sanitizePendingQuestion(data.pendingQuestion, id);
|
|
12781
|
+
if (data.pendingQuestion && pendingQuestion && pendingQuestion.questions.length === 0) {
|
|
12782
|
+
warnMalformedSessionPayload("Pending question normalized to zero renderable questions", {
|
|
12783
|
+
sessionID: id,
|
|
12784
|
+
checkpointID: pendingQuestion.id
|
|
12785
|
+
});
|
|
12786
|
+
}
|
|
12347
12787
|
return {
|
|
12348
12788
|
session: toSession(data.session),
|
|
12349
12789
|
turns,
|
|
12350
|
-
pendingQuestion
|
|
12790
|
+
pendingQuestion
|
|
12351
12791
|
};
|
|
12352
12792
|
} catch (err) {
|
|
12353
12793
|
if (isSessionNotFoundError(err)) {
|
|
@@ -12379,27 +12819,12 @@ var HttpDataSource = class {
|
|
|
12379
12819
|
return { artifacts: [] };
|
|
12380
12820
|
}
|
|
12381
12821
|
validateFileCount(0, files.length, 10);
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
validateAttachmentFile(file);
|
|
12385
|
-
const base64Data = await convertToBase64(file);
|
|
12386
|
-
attachments.push({
|
|
12387
|
-
clientKey: crypto.randomUUID(),
|
|
12388
|
-
filename: file.name,
|
|
12389
|
-
mimeType: file.type,
|
|
12390
|
-
sizeBytes: file.size,
|
|
12391
|
-
base64Data
|
|
12392
|
-
});
|
|
12393
|
-
}
|
|
12822
|
+
files.forEach((file) => validateAttachmentFile(file));
|
|
12823
|
+
const uploads = await Promise.all(files.map((file) => this.uploadFile(file)));
|
|
12394
12824
|
const data = await this.callRPC("bichat.session.uploadArtifacts", {
|
|
12395
12825
|
sessionId,
|
|
12396
|
-
attachments:
|
|
12397
|
-
|
|
12398
|
-
// Backend will assign ID
|
|
12399
|
-
filename: a.filename,
|
|
12400
|
-
mimeType: a.mimeType,
|
|
12401
|
-
sizeBytes: a.sizeBytes,
|
|
12402
|
-
base64Data: a.base64Data
|
|
12826
|
+
attachments: uploads.map((upload) => ({
|
|
12827
|
+
uploadId: upload.id
|
|
12403
12828
|
}))
|
|
12404
12829
|
});
|
|
12405
12830
|
return {
|
|
@@ -12430,22 +12855,21 @@ var HttpDataSource = class {
|
|
|
12430
12855
|
signal.addEventListener("abort", onExternalAbort);
|
|
12431
12856
|
}
|
|
12432
12857
|
const url = `${this.config.baseUrl}${this.config.streamEndpoint}`;
|
|
12433
|
-
const payload = {
|
|
12434
|
-
sessionId,
|
|
12435
|
-
content,
|
|
12436
|
-
debugMode: options?.debugMode ?? false,
|
|
12437
|
-
replaceFromMessageId: options?.replaceFromMessageID,
|
|
12438
|
-
attachments: attachments.map((a) => ({
|
|
12439
|
-
filename: a.filename,
|
|
12440
|
-
mimeType: a.mimeType,
|
|
12441
|
-
sizeBytes: a.sizeBytes,
|
|
12442
|
-
base64Data: a.base64Data,
|
|
12443
|
-
url: a.url
|
|
12444
|
-
}))
|
|
12445
|
-
};
|
|
12446
12858
|
let connectionTimeoutID;
|
|
12447
12859
|
let connectionTimedOut = false;
|
|
12448
12860
|
try {
|
|
12861
|
+
const uploads = await Promise.all(
|
|
12862
|
+
attachments.map((attachment) => this.ensureAttachmentUpload(attachment))
|
|
12863
|
+
);
|
|
12864
|
+
const payload = {
|
|
12865
|
+
sessionId,
|
|
12866
|
+
content,
|
|
12867
|
+
debugMode: options?.debugMode ?? false,
|
|
12868
|
+
replaceFromMessageId: options?.replaceFromMessageID,
|
|
12869
|
+
attachments: uploads.map((upload) => ({
|
|
12870
|
+
uploadId: upload.id
|
|
12871
|
+
}))
|
|
12872
|
+
};
|
|
12449
12873
|
const timeoutMs = this.config.timeout ?? 0;
|
|
12450
12874
|
if (timeoutMs > 0) {
|
|
12451
12875
|
connectionTimeoutID = setTimeout(() => {
|