@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.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import React, { createContext, lazy, memo, forwardRef, useState, useRef, useMemo
|
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import ReactApexChart from 'react-apexcharts';
|
|
4
4
|
import ApexCharts from 'apexcharts';
|
|
5
|
-
import { X, Bug, ArrowUp, ArrowDown, Stack, Paperclip, PaperPlaneRight, CircleNotch, ArrowUUpLeft, PencilSimple, Check, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree, Warning, ArrowClockwise, Image, ArrowCounterClockwise, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, MagnifyingGlass, WarningCircle, CaretDown, Copy, FilePdf, FileXls, FileCsv, FileDoc, FileCode, FileText, File, ChartBar, DownloadSimple, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, Wrench, ClockCounterClockwise, Lightbulb, Package, Plus, ArrowsCounterClockwise, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, ArrowSquareOut, SpinnerGap, FloppyDisk, Sidebar } from '@phosphor-icons/react';
|
|
5
|
+
import { X, Bug, ArrowUp, ArrowDown, Stack, Paperclip, PaperPlaneRight, CircleNotch, ArrowUUpLeft, PencilSimple, Check, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree, Warning, ArrowClockwise, Image, ArrowCounterClockwise, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, MagnifyingGlass, WarningCircle, CaretDown, Copy, FilePdf, FileXls, FileCsv, FileDoc, FileCode, FileText, File as File$1, ChartBar, DownloadSimple, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, Wrench, ClockCounterClockwise, Lightbulb, Package, Plus, ArrowsCounterClockwise, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, ArrowSquareOut, SpinnerGap, FloppyDisk, Sidebar } from '@phosphor-icons/react';
|
|
6
6
|
import { Prism } from 'react-syntax-highlighter';
|
|
7
7
|
import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
8
8
|
import ReactMarkdown from 'react-markdown';
|
|
@@ -1508,6 +1508,10 @@ var ChatMachine = class {
|
|
|
1508
1508
|
}
|
|
1509
1509
|
/** Sets turns from fetch, preserving pending user-only turns if server hasn't caught up. */
|
|
1510
1510
|
_setTurnsFromFetch(fetchedTurns) {
|
|
1511
|
+
if (!Array.isArray(fetchedTurns)) {
|
|
1512
|
+
console.warn("[ChatMachine] Ignoring malformed turns payload from fetchSession");
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1511
1515
|
const prev = this.state.messaging.turns;
|
|
1512
1516
|
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1513
1517
|
if (hasPendingUserOnly && (!fetchedTurns || fetchedTurns.length === 0)) {
|
|
@@ -1897,6 +1901,7 @@ var ChatMachine = class {
|
|
|
1897
1901
|
const curSessionId = this.state.session.currentSessionId;
|
|
1898
1902
|
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1899
1903
|
if (!curSessionId || !curPendingQuestion) return;
|
|
1904
|
+
const previousTurns = this.state.messaging.turns;
|
|
1900
1905
|
this._updateMessaging({ loading: true });
|
|
1901
1906
|
this._updateSession({ error: null, errorRetryable: false });
|
|
1902
1907
|
const previousPendingQuestion = curPendingQuestion;
|
|
@@ -1914,19 +1919,28 @@ var ChatMachine = class {
|
|
|
1914
1919
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1915
1920
|
if (this.disposed) return;
|
|
1916
1921
|
if (fetchResult) {
|
|
1917
|
-
this.
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1922
|
+
this._updateSession({ session: fetchResult.session });
|
|
1923
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1924
|
+
const hasMalformedRefresh = previousTurns.length > 0 && Array.isArray(fetchResult.turns) && fetchResult.turns.length === 0;
|
|
1925
|
+
if (hasMalformedRefresh) {
|
|
1926
|
+
console.warn("[ChatMachine] Preserving previous turns due to empty post-HITL refetch payload", {
|
|
1927
|
+
sessionId: curSessionId,
|
|
1928
|
+
previousTurnCount: previousTurns.length
|
|
1929
|
+
});
|
|
1930
|
+
this._updateSession({
|
|
1931
|
+
error: "Failed to fully refresh session. Showing last known messages.",
|
|
1932
|
+
errorRetryable: true
|
|
1933
|
+
});
|
|
1934
|
+
} else {
|
|
1935
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1936
|
+
}
|
|
1921
1937
|
} else {
|
|
1922
|
-
this.
|
|
1923
|
-
this._updateSession({ error: "Failed to load updated session", errorRetryable: false });
|
|
1938
|
+
this._updateSession({ error: "Failed to load updated session", errorRetryable: true });
|
|
1924
1939
|
}
|
|
1925
1940
|
} catch (fetchErr) {
|
|
1926
1941
|
if (this.disposed) return;
|
|
1927
|
-
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1928
1942
|
const normalized = normalizeRPCError(fetchErr, "Failed to load updated session");
|
|
1929
|
-
this._updateSession({ error: normalized.userMessage, errorRetryable:
|
|
1943
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: true });
|
|
1930
1944
|
}
|
|
1931
1945
|
}
|
|
1932
1946
|
} else {
|
|
@@ -2448,7 +2462,7 @@ function getFileVisual(mimeType, filename) {
|
|
|
2448
2462
|
};
|
|
2449
2463
|
}
|
|
2450
2464
|
return {
|
|
2451
|
-
icon: File,
|
|
2465
|
+
icon: File$1,
|
|
2452
2466
|
iconColor: "text-gray-400 dark:text-gray-500",
|
|
2453
2467
|
bgColor: "bg-gray-100 dark:bg-gray-800",
|
|
2454
2468
|
label: (mime.split("/")[1] || "FILE").toUpperCase().slice(0, 4)
|
|
@@ -3286,7 +3300,7 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3286
3300
|
const [currentStep, setCurrentStep] = useState(0);
|
|
3287
3301
|
const [answers, setAnswers] = useState({});
|
|
3288
3302
|
const [otherTexts, setOtherTexts] = useState({});
|
|
3289
|
-
const questions = pendingQuestion.questions;
|
|
3303
|
+
const questions = Array.isArray(pendingQuestion.questions) ? pendingQuestion.questions : [];
|
|
3290
3304
|
const currentQuestion = questions[currentStep];
|
|
3291
3305
|
const isLastStep = currentStep === questions.length - 1;
|
|
3292
3306
|
const isFirstStep = currentStep === 0;
|
|
@@ -3380,9 +3394,31 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3380
3394
|
e.preventDefault();
|
|
3381
3395
|
handleNext();
|
|
3382
3396
|
};
|
|
3383
|
-
if (!currentQuestion)
|
|
3397
|
+
if (!currentQuestion) {
|
|
3398
|
+
return /* @__PURE__ */ 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: [
|
|
3399
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-800 dark:text-gray-200", children: t("BiChat.Error.SomethingWentWrong") }),
|
|
3400
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Error.UnexpectedError") }),
|
|
3401
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
3402
|
+
"button",
|
|
3403
|
+
{
|
|
3404
|
+
type: "button",
|
|
3405
|
+
onClick: handleRejectPendingQuestion,
|
|
3406
|
+
disabled: loading,
|
|
3407
|
+
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",
|
|
3408
|
+
children: [
|
|
3409
|
+
/* @__PURE__ */ jsx(X, { size: 14, weight: "bold" }),
|
|
3410
|
+
t("BiChat.InlineQuestion.Dismiss")
|
|
3411
|
+
]
|
|
3412
|
+
}
|
|
3413
|
+
) })
|
|
3414
|
+
] });
|
|
3415
|
+
}
|
|
3384
3416
|
const isMultiSelect = currentQuestion.type === "MULTIPLE_CHOICE";
|
|
3385
|
-
const options = currentQuestion.options || []
|
|
3417
|
+
const options = (currentQuestion.options || []).filter((option) => Boolean(option && typeof option.label === "string")).map((option, index) => ({
|
|
3418
|
+
id: option.id || `${currentQuestion.id}-option-${index}`,
|
|
3419
|
+
label: option.label,
|
|
3420
|
+
value: option.value || option.label
|
|
3421
|
+
}));
|
|
3386
3422
|
const isOtherSelected = currentAnswer?.customText !== void 0;
|
|
3387
3423
|
const canProceed = isCurrentAnswerValid();
|
|
3388
3424
|
return /* @__PURE__ */ 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__ */ jsxs("form", { onSubmit: handleSubmit, children: [
|
|
@@ -4296,7 +4332,8 @@ function TurnBubble({
|
|
|
4296
4332
|
userTurn: classNames?.userTurn ?? defaultClassNames3.userTurn,
|
|
4297
4333
|
assistantTurn: classNames?.assistantTurn ?? defaultClassNames3.assistantTurn
|
|
4298
4334
|
};
|
|
4299
|
-
const
|
|
4335
|
+
const userContent = typeof turn.userTurn?.content === "string" ? turn.userTurn.content : "";
|
|
4336
|
+
const isSystemSummaryTurn = userContent.trim() === "" && turn.assistantTurn?.role === "system";
|
|
4300
4337
|
return /* @__PURE__ */ jsxs("div", { className: classes.root, "data-turn-id": turn.id, children: [
|
|
4301
4338
|
!isSystemSummaryTurn && /* @__PURE__ */ jsx("div", { className: classes.userTurn, children: renderUserTurn ? renderUserTurn(turn) : /* @__PURE__ */ jsx(
|
|
4302
4339
|
UserTurnView,
|
|
@@ -8074,12 +8111,63 @@ function DefaultErrorContent({
|
|
|
8074
8111
|
] })
|
|
8075
8112
|
] });
|
|
8076
8113
|
}
|
|
8114
|
+
function StaticEmergencyErrorContent({
|
|
8115
|
+
error,
|
|
8116
|
+
onReset
|
|
8117
|
+
}) {
|
|
8118
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center p-8 text-center min-h-[200px]", children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center", children: [
|
|
8119
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mb-5", children: [
|
|
8120
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-red-100 scale-150 blur-md" }),
|
|
8121
|
+
/* @__PURE__ */ 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__ */ jsx(WarningCircle, { size: 28, className: "text-red-500", weight: "fill" }) })
|
|
8122
|
+
] }),
|
|
8123
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-1.5", children: "Something went wrong" }),
|
|
8124
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mb-5 max-w-md leading-relaxed", children: error?.message || "An unexpected UI error occurred." }),
|
|
8125
|
+
onReset && /* @__PURE__ */ jsxs(
|
|
8126
|
+
"button",
|
|
8127
|
+
{
|
|
8128
|
+
type: "button",
|
|
8129
|
+
onClick: onReset,
|
|
8130
|
+
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",
|
|
8131
|
+
children: [
|
|
8132
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, weight: "bold" }),
|
|
8133
|
+
"Try again"
|
|
8134
|
+
]
|
|
8135
|
+
}
|
|
8136
|
+
)
|
|
8137
|
+
] }) });
|
|
8138
|
+
}
|
|
8139
|
+
var FallbackGuard = class extends Component {
|
|
8140
|
+
constructor(props) {
|
|
8141
|
+
super(props);
|
|
8142
|
+
this.state = { fallbackFailed: false };
|
|
8143
|
+
}
|
|
8144
|
+
static getDerivedStateFromError() {
|
|
8145
|
+
return { fallbackFailed: true };
|
|
8146
|
+
}
|
|
8147
|
+
componentDidCatch(error, errorInfo) {
|
|
8148
|
+
this.props.onFallbackError?.(error, errorInfo);
|
|
8149
|
+
}
|
|
8150
|
+
render() {
|
|
8151
|
+
if (this.state.fallbackFailed) {
|
|
8152
|
+
return /* @__PURE__ */ jsx(StaticEmergencyErrorContent, { error: this.props.primaryError, onReset: this.props.onReset });
|
|
8153
|
+
}
|
|
8154
|
+
return this.props.renderFallback();
|
|
8155
|
+
}
|
|
8156
|
+
};
|
|
8077
8157
|
var ErrorBoundary = class extends Component {
|
|
8078
8158
|
constructor(props) {
|
|
8079
8159
|
super(props);
|
|
8080
8160
|
this.handleReset = () => {
|
|
8081
8161
|
this.setState({ hasError: false, error: null });
|
|
8082
8162
|
};
|
|
8163
|
+
this.handleFallbackError = (error, errorInfo) => {
|
|
8164
|
+
console.error("React Error Boundary fallback crashed:", {
|
|
8165
|
+
primaryError: this.state.error,
|
|
8166
|
+
fallbackError: error,
|
|
8167
|
+
errorInfo
|
|
8168
|
+
});
|
|
8169
|
+
this.props.onError?.(error, errorInfo);
|
|
8170
|
+
};
|
|
8083
8171
|
this.state = { hasError: false, error: null };
|
|
8084
8172
|
}
|
|
8085
8173
|
static getDerivedStateFromError(error) {
|
|
@@ -8091,13 +8179,24 @@ var ErrorBoundary = class extends Component {
|
|
|
8091
8179
|
}
|
|
8092
8180
|
render() {
|
|
8093
8181
|
if (this.state.hasError) {
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8182
|
+
return /* @__PURE__ */ jsx(
|
|
8183
|
+
FallbackGuard,
|
|
8184
|
+
{
|
|
8185
|
+
primaryError: this.state.error,
|
|
8186
|
+
onReset: this.handleReset,
|
|
8187
|
+
onFallbackError: this.handleFallbackError,
|
|
8188
|
+
renderFallback: () => {
|
|
8189
|
+
if (this.props.fallback) {
|
|
8190
|
+
if (typeof this.props.fallback === "function") {
|
|
8191
|
+
return this.props.fallback(this.state.error, this.handleReset);
|
|
8192
|
+
}
|
|
8193
|
+
return this.props.fallback;
|
|
8194
|
+
}
|
|
8195
|
+
return /* @__PURE__ */ jsx(DefaultErrorContent, { error: this.state.error, onReset: this.handleReset });
|
|
8196
|
+
}
|
|
8197
|
+
},
|
|
8198
|
+
`${this.state.error?.name ?? "Error"}:${this.state.error?.message ?? ""}`
|
|
8199
|
+
);
|
|
8101
8200
|
}
|
|
8102
8201
|
return this.props.children;
|
|
8103
8202
|
}
|
|
@@ -12056,6 +12155,7 @@ function toSessionArtifact(artifact) {
|
|
|
12056
12155
|
id: artifact.id,
|
|
12057
12156
|
sessionId: artifact.sessionId,
|
|
12058
12157
|
messageId: artifact.messageId,
|
|
12158
|
+
uploadId: artifact.uploadId,
|
|
12059
12159
|
type: artifact.type,
|
|
12060
12160
|
name: artifact.name,
|
|
12061
12161
|
description: artifact.description,
|
|
@@ -12066,21 +12166,272 @@ function toSessionArtifact(artifact) {
|
|
|
12066
12166
|
createdAt: artifact.createdAt
|
|
12067
12167
|
};
|
|
12068
12168
|
}
|
|
12069
|
-
function
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12169
|
+
function warnMalformedSessionPayload(message, details) {
|
|
12170
|
+
console.warn(`[BiChat] ${message}`, details || {});
|
|
12171
|
+
}
|
|
12172
|
+
function readString(value, fallback = "") {
|
|
12173
|
+
return typeof value === "string" ? value : fallback;
|
|
12174
|
+
}
|
|
12175
|
+
function readNonEmptyString(value) {
|
|
12176
|
+
if (typeof value !== "string") return null;
|
|
12177
|
+
const trimmed = value.trim();
|
|
12178
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12179
|
+
}
|
|
12180
|
+
function readFiniteNumber(value, fallback = 0) {
|
|
12181
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
12182
|
+
}
|
|
12183
|
+
function readOptionalFiniteNumber(value) {
|
|
12184
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
12185
|
+
}
|
|
12186
|
+
function normalizeQuestionType(rawType) {
|
|
12187
|
+
const normalized = readString(rawType).trim().toUpperCase().replace(/[\s-]+/g, "_");
|
|
12188
|
+
return normalized === "MULTIPLE_CHOICE" ? "MULTIPLE_CHOICE" : "SINGLE_CHOICE";
|
|
12189
|
+
}
|
|
12190
|
+
function normalizeMessageRole(rawRole) {
|
|
12191
|
+
const normalized = readString(rawRole).trim().toLowerCase();
|
|
12192
|
+
if (normalized === "user" /* User */) return "user" /* User */;
|
|
12193
|
+
if (normalized === "system" /* System */) return "system" /* System */;
|
|
12194
|
+
if (normalized === "tool" /* Tool */) return "tool" /* Tool */;
|
|
12195
|
+
return "assistant" /* Assistant */;
|
|
12196
|
+
}
|
|
12197
|
+
function sanitizeAttachment(rawAttachment, turnId, index) {
|
|
12198
|
+
if (!isRecord(rawAttachment)) {
|
|
12199
|
+
warnMalformedSessionPayload("Dropped malformed attachment entry", { turnId, index });
|
|
12200
|
+
return null;
|
|
12201
|
+
}
|
|
12202
|
+
const filename = readString(rawAttachment.filename, "attachment");
|
|
12203
|
+
const mimeType = readString(rawAttachment.mimeType, "application/octet-stream");
|
|
12204
|
+
const id = readNonEmptyString(rawAttachment.id) || void 0;
|
|
12205
|
+
const clientKey = readNonEmptyString(rawAttachment.clientKey) || id || `${turnId}-attachment-${index}`;
|
|
12206
|
+
return {
|
|
12207
|
+
id,
|
|
12208
|
+
clientKey,
|
|
12209
|
+
filename,
|
|
12210
|
+
mimeType,
|
|
12211
|
+
sizeBytes: readFiniteNumber(rawAttachment.sizeBytes),
|
|
12212
|
+
uploadId: readOptionalFiniteNumber(rawAttachment.uploadId),
|
|
12213
|
+
base64Data: readNonEmptyString(rawAttachment.base64Data) || void 0,
|
|
12214
|
+
url: readNonEmptyString(rawAttachment.url) || void 0,
|
|
12215
|
+
preview: readNonEmptyString(rawAttachment.preview) || void 0
|
|
12216
|
+
};
|
|
12217
|
+
}
|
|
12218
|
+
function sanitizeUserAttachments(rawAttachments, turnId) {
|
|
12219
|
+
if (!Array.isArray(rawAttachments)) return [];
|
|
12220
|
+
const result = [];
|
|
12221
|
+
for (let i = 0; i < rawAttachments.length; i++) {
|
|
12222
|
+
const sanitized = sanitizeAttachment(rawAttachments[i], turnId, i);
|
|
12223
|
+
if (sanitized) result.push(sanitized);
|
|
12224
|
+
}
|
|
12225
|
+
return result;
|
|
12226
|
+
}
|
|
12227
|
+
function sanitizeAssistantArtifacts(rawArtifacts, turnId) {
|
|
12228
|
+
if (!Array.isArray(rawArtifacts)) return [];
|
|
12229
|
+
const artifacts = [];
|
|
12230
|
+
for (let i = 0; i < rawArtifacts.length; i++) {
|
|
12231
|
+
const raw = rawArtifacts[i];
|
|
12232
|
+
if (!isRecord(raw)) {
|
|
12233
|
+
warnMalformedSessionPayload("Dropped malformed assistant artifact", { turnId, index: i });
|
|
12234
|
+
continue;
|
|
12235
|
+
}
|
|
12236
|
+
const type = readString(raw.type).toLowerCase();
|
|
12237
|
+
if (type !== "excel" && type !== "pdf") {
|
|
12238
|
+
continue;
|
|
12239
|
+
}
|
|
12240
|
+
const url = readNonEmptyString(raw.url);
|
|
12241
|
+
if (!url) {
|
|
12242
|
+
warnMalformedSessionPayload("Dropped assistant artifact without url", { turnId, index: i });
|
|
12243
|
+
continue;
|
|
12244
|
+
}
|
|
12245
|
+
artifacts.push({
|
|
12246
|
+
type,
|
|
12247
|
+
filename: readString(raw.filename, "download"),
|
|
12248
|
+
url,
|
|
12249
|
+
sizeReadable: readNonEmptyString(raw.sizeReadable) || void 0,
|
|
12250
|
+
rowCount: typeof raw.rowCount === "number" && Number.isFinite(raw.rowCount) ? raw.rowCount : void 0,
|
|
12251
|
+
description: readNonEmptyString(raw.description) || void 0
|
|
12252
|
+
});
|
|
12253
|
+
}
|
|
12254
|
+
return artifacts;
|
|
12255
|
+
}
|
|
12256
|
+
function sanitizeAssistantTurn(rawAssistantTurn, fallbackCreatedAt, turnId) {
|
|
12257
|
+
if (rawAssistantTurn == null) return void 0;
|
|
12258
|
+
if (!isRecord(rawAssistantTurn)) {
|
|
12259
|
+
warnMalformedSessionPayload("Dropped malformed assistant turn payload", { turnId });
|
|
12260
|
+
return void 0;
|
|
12261
|
+
}
|
|
12262
|
+
const assistantID = readNonEmptyString(rawAssistantTurn.id);
|
|
12263
|
+
if (!assistantID) {
|
|
12264
|
+
warnMalformedSessionPayload("Dropped assistant turn without id", { turnId });
|
|
12265
|
+
return void 0;
|
|
12266
|
+
}
|
|
12267
|
+
const citations = Array.isArray(rawAssistantTurn.citations) ? rawAssistantTurn.citations.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12268
|
+
id: readString(item.id, `${assistantID}-citation-${index}`),
|
|
12269
|
+
type: readString(item.type),
|
|
12270
|
+
title: readString(item.title),
|
|
12271
|
+
url: readString(item.url),
|
|
12272
|
+
startIndex: readFiniteNumber(item.startIndex),
|
|
12273
|
+
endIndex: readFiniteNumber(item.endIndex),
|
|
12274
|
+
excerpt: readNonEmptyString(item.excerpt) || void 0
|
|
12275
|
+
})) : [];
|
|
12276
|
+
const toolCalls = Array.isArray(rawAssistantTurn.toolCalls) ? rawAssistantTurn.toolCalls.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12277
|
+
id: readString(item.id, `${assistantID}-tool-${index}`),
|
|
12278
|
+
name: readString(item.name),
|
|
12279
|
+
arguments: readString(item.arguments),
|
|
12280
|
+
result: readNonEmptyString(item.result) || void 0,
|
|
12281
|
+
error: readNonEmptyString(item.error) || void 0,
|
|
12282
|
+
durationMs: readFiniteNumber(item.durationMs)
|
|
12283
|
+
})) : [];
|
|
12284
|
+
const codeOutputs = Array.isArray(rawAssistantTurn.codeOutputs) ? rawAssistantTurn.codeOutputs.filter((item) => isRecord(item)).map((item) => ({
|
|
12285
|
+
type: (() => {
|
|
12286
|
+
const normalizedType = readString(item.type, "text").toLowerCase();
|
|
12287
|
+
if (normalizedType === "image" || normalizedType === "error") return normalizedType;
|
|
12288
|
+
return "text";
|
|
12289
|
+
})(),
|
|
12290
|
+
content: readString(item.content),
|
|
12291
|
+
filename: readNonEmptyString(item.filename) || void 0,
|
|
12292
|
+
mimeType: readNonEmptyString(item.mimeType) || void 0,
|
|
12293
|
+
sizeBytes: readOptionalFiniteNumber(item.sizeBytes)
|
|
12294
|
+
})) : [];
|
|
12295
|
+
const debugTrace = isRecord(rawAssistantTurn.debug) ? {
|
|
12296
|
+
generationMs: readOptionalFiniteNumber(rawAssistantTurn.debug.generationMs),
|
|
12297
|
+
usage: isRecord(rawAssistantTurn.debug.usage) ? {
|
|
12298
|
+
promptTokens: readFiniteNumber(rawAssistantTurn.debug.usage.promptTokens),
|
|
12299
|
+
completionTokens: readFiniteNumber(rawAssistantTurn.debug.usage.completionTokens),
|
|
12300
|
+
totalTokens: readFiniteNumber(rawAssistantTurn.debug.usage.totalTokens),
|
|
12301
|
+
cachedTokens: readOptionalFiniteNumber(rawAssistantTurn.debug.usage.cachedTokens),
|
|
12302
|
+
cost: readOptionalFiniteNumber(rawAssistantTurn.debug.usage.cost)
|
|
12303
|
+
} : void 0,
|
|
12304
|
+
tools: Array.isArray(rawAssistantTurn.debug.tools) ? rawAssistantTurn.debug.tools.filter((tool) => isRecord(tool)).map((tool) => ({
|
|
12305
|
+
callId: readNonEmptyString(tool.callId) || void 0,
|
|
12306
|
+
name: readString(tool.name),
|
|
12307
|
+
arguments: readNonEmptyString(tool.arguments) || void 0,
|
|
12308
|
+
result: readNonEmptyString(tool.result) || void 0,
|
|
12309
|
+
error: readNonEmptyString(tool.error) || void 0,
|
|
12310
|
+
durationMs: readOptionalFiniteNumber(tool.durationMs)
|
|
12311
|
+
})) : []
|
|
12312
|
+
} : void 0;
|
|
12313
|
+
return {
|
|
12314
|
+
id: assistantID,
|
|
12315
|
+
role: normalizeMessageRole(rawAssistantTurn.role),
|
|
12316
|
+
content: readString(rawAssistantTurn.content),
|
|
12317
|
+
explanation: readNonEmptyString(rawAssistantTurn.explanation) || void 0,
|
|
12318
|
+
citations,
|
|
12319
|
+
toolCalls,
|
|
12320
|
+
chartData: void 0,
|
|
12321
|
+
artifacts: sanitizeAssistantArtifacts(rawAssistantTurn.artifacts, turnId),
|
|
12322
|
+
codeOutputs,
|
|
12323
|
+
debug: debugTrace,
|
|
12324
|
+
createdAt: readString(rawAssistantTurn.createdAt, fallbackCreatedAt)
|
|
12325
|
+
};
|
|
12326
|
+
}
|
|
12327
|
+
function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
|
|
12328
|
+
if (!isRecord(rawTurn)) {
|
|
12329
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (not an object)", { index });
|
|
12330
|
+
return null;
|
|
12331
|
+
}
|
|
12332
|
+
if (!isRecord(rawTurn.userTurn)) {
|
|
12333
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn)", { index });
|
|
12334
|
+
return null;
|
|
12335
|
+
}
|
|
12336
|
+
const userTurnID = readNonEmptyString(rawTurn.userTurn.id);
|
|
12337
|
+
if (!userTurnID) {
|
|
12338
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn id)", { index });
|
|
12339
|
+
return null;
|
|
12340
|
+
}
|
|
12341
|
+
const turnID = readString(rawTurn.id, userTurnID);
|
|
12342
|
+
const createdAt = readString(
|
|
12343
|
+
rawTurn.createdAt,
|
|
12344
|
+
readString(rawTurn.userTurn.createdAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
12345
|
+
);
|
|
12081
12346
|
return {
|
|
12082
|
-
id:
|
|
12083
|
-
|
|
12347
|
+
id: turnID,
|
|
12348
|
+
sessionId: readString(rawTurn.sessionId, fallbackSessionID),
|
|
12349
|
+
userTurn: {
|
|
12350
|
+
id: userTurnID,
|
|
12351
|
+
content: readString(rawTurn.userTurn.content),
|
|
12352
|
+
attachments: sanitizeUserAttachments(rawTurn.userTurn.attachments, turnID),
|
|
12353
|
+
createdAt: readString(rawTurn.userTurn.createdAt, createdAt)
|
|
12354
|
+
},
|
|
12355
|
+
assistantTurn: sanitizeAssistantTurn(rawTurn.assistantTurn, createdAt, turnID),
|
|
12356
|
+
createdAt
|
|
12357
|
+
};
|
|
12358
|
+
}
|
|
12359
|
+
function sanitizeConversationTurns(rawTurns, sessionID) {
|
|
12360
|
+
if (!Array.isArray(rawTurns)) {
|
|
12361
|
+
warnMalformedSessionPayload("Session payload contained non-array turns field", { sessionID });
|
|
12362
|
+
return [];
|
|
12363
|
+
}
|
|
12364
|
+
const turns = [];
|
|
12365
|
+
let dropped = 0;
|
|
12366
|
+
for (let i = 0; i < rawTurns.length; i++) {
|
|
12367
|
+
const sanitizedTurn = sanitizeConversationTurn(rawTurns[i], i, sessionID);
|
|
12368
|
+
if (sanitizedTurn) {
|
|
12369
|
+
turns.push(sanitizedTurn);
|
|
12370
|
+
} else {
|
|
12371
|
+
dropped++;
|
|
12372
|
+
}
|
|
12373
|
+
}
|
|
12374
|
+
if (dropped > 0) {
|
|
12375
|
+
warnMalformedSessionPayload("Dropped malformed turns from session payload", {
|
|
12376
|
+
sessionID,
|
|
12377
|
+
dropped,
|
|
12378
|
+
total: rawTurns.length
|
|
12379
|
+
});
|
|
12380
|
+
}
|
|
12381
|
+
return turns;
|
|
12382
|
+
}
|
|
12383
|
+
function sanitizePendingQuestion(rawPendingQuestion, sessionID) {
|
|
12384
|
+
if (!rawPendingQuestion) return null;
|
|
12385
|
+
const checkpointID = readNonEmptyString(rawPendingQuestion.checkpointId);
|
|
12386
|
+
if (!checkpointID) {
|
|
12387
|
+
warnMalformedSessionPayload("Dropped malformed pendingQuestion without checkpointId", { sessionID });
|
|
12388
|
+
return null;
|
|
12389
|
+
}
|
|
12390
|
+
if (!Array.isArray(rawPendingQuestion.questions)) {
|
|
12391
|
+
warnMalformedSessionPayload("Pending question had non-array questions payload", {
|
|
12392
|
+
sessionID,
|
|
12393
|
+
checkpointID
|
|
12394
|
+
});
|
|
12395
|
+
}
|
|
12396
|
+
const questions = Array.isArray(rawPendingQuestion.questions) ? rawPendingQuestion.questions.filter((question) => {
|
|
12397
|
+
if (!question || !isRecord(question)) {
|
|
12398
|
+
warnMalformedSessionPayload("Dropped malformed question from pendingQuestion", {
|
|
12399
|
+
sessionID,
|
|
12400
|
+
checkpointID
|
|
12401
|
+
});
|
|
12402
|
+
return false;
|
|
12403
|
+
}
|
|
12404
|
+
return true;
|
|
12405
|
+
}).map((question, index) => {
|
|
12406
|
+
const questionID = readString(question.id, `${checkpointID}-q-${index}`);
|
|
12407
|
+
const options = Array.isArray(question.options) ? question.options.filter((option) => {
|
|
12408
|
+
if (!option || !isRecord(option)) {
|
|
12409
|
+
warnMalformedSessionPayload("Dropped malformed pendingQuestion option", {
|
|
12410
|
+
sessionID,
|
|
12411
|
+
checkpointID,
|
|
12412
|
+
questionID
|
|
12413
|
+
});
|
|
12414
|
+
return false;
|
|
12415
|
+
}
|
|
12416
|
+
return true;
|
|
12417
|
+
}).map((option, optionIndex) => {
|
|
12418
|
+
const label = readString(option.label);
|
|
12419
|
+
return {
|
|
12420
|
+
id: readString(option.id, `${questionID}-opt-${optionIndex}`),
|
|
12421
|
+
label,
|
|
12422
|
+
value: label
|
|
12423
|
+
};
|
|
12424
|
+
}) : [];
|
|
12425
|
+
return {
|
|
12426
|
+
id: questionID,
|
|
12427
|
+
text: readString(question.text),
|
|
12428
|
+
type: normalizeQuestionType(question.type),
|
|
12429
|
+
options
|
|
12430
|
+
};
|
|
12431
|
+
}) : [];
|
|
12432
|
+
return {
|
|
12433
|
+
id: checkpointID,
|
|
12434
|
+
turnId: readString(rawPendingQuestion.turnId),
|
|
12084
12435
|
questions,
|
|
12085
12436
|
status: "PENDING"
|
|
12086
12437
|
};
|
|
@@ -12276,6 +12627,7 @@ var HttpDataSource = class {
|
|
|
12276
12627
|
this.abortController = null;
|
|
12277
12628
|
this.config = {
|
|
12278
12629
|
streamEndpoint: "/stream",
|
|
12630
|
+
uploadEndpoint: "/api/uploads",
|
|
12279
12631
|
timeout: 12e4,
|
|
12280
12632
|
...config
|
|
12281
12633
|
};
|
|
@@ -12311,6 +12663,83 @@ var HttpDataSource = class {
|
|
|
12311
12663
|
}
|
|
12312
12664
|
return headers;
|
|
12313
12665
|
}
|
|
12666
|
+
createUploadHeaders(additionalHeaders) {
|
|
12667
|
+
const headers = new Headers({
|
|
12668
|
+
...this.config.headers,
|
|
12669
|
+
...additionalHeaders
|
|
12670
|
+
});
|
|
12671
|
+
const csrfToken = this.getCSRFToken();
|
|
12672
|
+
if (csrfToken) {
|
|
12673
|
+
headers.set("X-CSRF-Token", csrfToken);
|
|
12674
|
+
}
|
|
12675
|
+
headers.delete("Content-Type");
|
|
12676
|
+
return headers;
|
|
12677
|
+
}
|
|
12678
|
+
async uploadFile(file) {
|
|
12679
|
+
const formData = new FormData();
|
|
12680
|
+
formData.append("file", file);
|
|
12681
|
+
const response = await fetch(`${this.config.baseUrl}${this.config.uploadEndpoint}`, {
|
|
12682
|
+
method: "POST",
|
|
12683
|
+
headers: this.createUploadHeaders(),
|
|
12684
|
+
body: formData
|
|
12685
|
+
});
|
|
12686
|
+
let payload = null;
|
|
12687
|
+
try {
|
|
12688
|
+
payload = await response.json();
|
|
12689
|
+
} catch {
|
|
12690
|
+
payload = null;
|
|
12691
|
+
}
|
|
12692
|
+
if (!response.ok) {
|
|
12693
|
+
const errorMessage = isRecord(payload) && typeof payload.error === "string" ? payload.error : `Upload failed: HTTP ${response.status}`;
|
|
12694
|
+
throw new Error(errorMessage);
|
|
12695
|
+
}
|
|
12696
|
+
if (!isRecord(payload) || typeof payload.id !== "number" || payload.id <= 0) {
|
|
12697
|
+
throw new Error("Upload failed: invalid response payload");
|
|
12698
|
+
}
|
|
12699
|
+
return {
|
|
12700
|
+
id: payload.id,
|
|
12701
|
+
url: typeof payload.url === "string" ? payload.url : "",
|
|
12702
|
+
path: typeof payload.path === "string" ? payload.path : "",
|
|
12703
|
+
name: typeof payload.name === "string" ? payload.name : file.name,
|
|
12704
|
+
mimetype: typeof payload.mimetype === "string" ? payload.mimetype : file.type,
|
|
12705
|
+
size: typeof payload.size === "number" && Number.isFinite(payload.size) ? payload.size : file.size
|
|
12706
|
+
};
|
|
12707
|
+
}
|
|
12708
|
+
async attachmentToFile(attachment) {
|
|
12709
|
+
if (attachment.base64Data && attachment.base64Data.trim().length > 0) {
|
|
12710
|
+
const base64Data = attachment.base64Data.trim();
|
|
12711
|
+
const dataUrl = base64Data.startsWith("data:") ? base64Data : `data:${attachment.mimeType || "application/octet-stream"};base64,${base64Data}`;
|
|
12712
|
+
const blob = await fetch(dataUrl).then((response) => response.blob());
|
|
12713
|
+
return new File([blob], attachment.filename, {
|
|
12714
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
12715
|
+
});
|
|
12716
|
+
}
|
|
12717
|
+
if (attachment.url) {
|
|
12718
|
+
const response = await fetch(attachment.url);
|
|
12719
|
+
if (!response.ok) {
|
|
12720
|
+
throw new Error(`Failed to read attachment source: HTTP ${response.status}`);
|
|
12721
|
+
}
|
|
12722
|
+
const blob = await response.blob();
|
|
12723
|
+
return new File([blob], attachment.filename, {
|
|
12724
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
12725
|
+
});
|
|
12726
|
+
}
|
|
12727
|
+
throw new Error(`Attachment "${attachment.filename}" has no uploadable data`);
|
|
12728
|
+
}
|
|
12729
|
+
async ensureAttachmentUpload(attachment) {
|
|
12730
|
+
if (typeof attachment.uploadId === "number" && attachment.uploadId > 0) {
|
|
12731
|
+
return {
|
|
12732
|
+
id: attachment.uploadId,
|
|
12733
|
+
url: attachment.url || "",
|
|
12734
|
+
path: "",
|
|
12735
|
+
name: attachment.filename,
|
|
12736
|
+
mimetype: attachment.mimeType,
|
|
12737
|
+
size: attachment.sizeBytes
|
|
12738
|
+
};
|
|
12739
|
+
}
|
|
12740
|
+
const file = await this.attachmentToFile(attachment);
|
|
12741
|
+
return this.uploadFile(file);
|
|
12742
|
+
}
|
|
12314
12743
|
async callRPC(method, params) {
|
|
12315
12744
|
return this.rpc.callTyped(method, params);
|
|
12316
12745
|
}
|
|
@@ -12333,11 +12762,22 @@ var HttpDataSource = class {
|
|
|
12333
12762
|
return { artifacts: [], hasMore: false, nextOffset: 0 };
|
|
12334
12763
|
})
|
|
12335
12764
|
]);
|
|
12336
|
-
const
|
|
12765
|
+
const sanitizedTurns = sanitizeConversationTurns(data.turns, id);
|
|
12766
|
+
const turns = attachArtifactsToTurns(
|
|
12767
|
+
normalizeTurns(sanitizedTurns),
|
|
12768
|
+
artifactsData.artifacts || []
|
|
12769
|
+
);
|
|
12770
|
+
const pendingQuestion = sanitizePendingQuestion(data.pendingQuestion, id);
|
|
12771
|
+
if (data.pendingQuestion && pendingQuestion && pendingQuestion.questions.length === 0) {
|
|
12772
|
+
warnMalformedSessionPayload("Pending question normalized to zero renderable questions", {
|
|
12773
|
+
sessionID: id,
|
|
12774
|
+
checkpointID: pendingQuestion.id
|
|
12775
|
+
});
|
|
12776
|
+
}
|
|
12337
12777
|
return {
|
|
12338
12778
|
session: toSession(data.session),
|
|
12339
12779
|
turns,
|
|
12340
|
-
pendingQuestion
|
|
12780
|
+
pendingQuestion
|
|
12341
12781
|
};
|
|
12342
12782
|
} catch (err) {
|
|
12343
12783
|
if (isSessionNotFoundError(err)) {
|
|
@@ -12369,27 +12809,12 @@ var HttpDataSource = class {
|
|
|
12369
12809
|
return { artifacts: [] };
|
|
12370
12810
|
}
|
|
12371
12811
|
validateFileCount(0, files.length, 10);
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
validateAttachmentFile(file);
|
|
12375
|
-
const base64Data = await convertToBase64(file);
|
|
12376
|
-
attachments.push({
|
|
12377
|
-
clientKey: crypto.randomUUID(),
|
|
12378
|
-
filename: file.name,
|
|
12379
|
-
mimeType: file.type,
|
|
12380
|
-
sizeBytes: file.size,
|
|
12381
|
-
base64Data
|
|
12382
|
-
});
|
|
12383
|
-
}
|
|
12812
|
+
files.forEach((file) => validateAttachmentFile(file));
|
|
12813
|
+
const uploads = await Promise.all(files.map((file) => this.uploadFile(file)));
|
|
12384
12814
|
const data = await this.callRPC("bichat.session.uploadArtifacts", {
|
|
12385
12815
|
sessionId,
|
|
12386
|
-
attachments:
|
|
12387
|
-
|
|
12388
|
-
// Backend will assign ID
|
|
12389
|
-
filename: a.filename,
|
|
12390
|
-
mimeType: a.mimeType,
|
|
12391
|
-
sizeBytes: a.sizeBytes,
|
|
12392
|
-
base64Data: a.base64Data
|
|
12816
|
+
attachments: uploads.map((upload) => ({
|
|
12817
|
+
uploadId: upload.id
|
|
12393
12818
|
}))
|
|
12394
12819
|
});
|
|
12395
12820
|
return {
|
|
@@ -12420,22 +12845,21 @@ var HttpDataSource = class {
|
|
|
12420
12845
|
signal.addEventListener("abort", onExternalAbort);
|
|
12421
12846
|
}
|
|
12422
12847
|
const url = `${this.config.baseUrl}${this.config.streamEndpoint}`;
|
|
12423
|
-
const payload = {
|
|
12424
|
-
sessionId,
|
|
12425
|
-
content,
|
|
12426
|
-
debugMode: options?.debugMode ?? false,
|
|
12427
|
-
replaceFromMessageId: options?.replaceFromMessageID,
|
|
12428
|
-
attachments: attachments.map((a) => ({
|
|
12429
|
-
filename: a.filename,
|
|
12430
|
-
mimeType: a.mimeType,
|
|
12431
|
-
sizeBytes: a.sizeBytes,
|
|
12432
|
-
base64Data: a.base64Data,
|
|
12433
|
-
url: a.url
|
|
12434
|
-
}))
|
|
12435
|
-
};
|
|
12436
12848
|
let connectionTimeoutID;
|
|
12437
12849
|
let connectionTimedOut = false;
|
|
12438
12850
|
try {
|
|
12851
|
+
const uploads = await Promise.all(
|
|
12852
|
+
attachments.map((attachment) => this.ensureAttachmentUpload(attachment))
|
|
12853
|
+
);
|
|
12854
|
+
const payload = {
|
|
12855
|
+
sessionId,
|
|
12856
|
+
content,
|
|
12857
|
+
debugMode: options?.debugMode ?? false,
|
|
12858
|
+
replaceFromMessageId: options?.replaceFromMessageID,
|
|
12859
|
+
attachments: uploads.map((upload) => ({
|
|
12860
|
+
uploadId: upload.id
|
|
12861
|
+
}))
|
|
12862
|
+
};
|
|
12439
12863
|
const timeoutMs = this.config.timeout ?? 0;
|
|
12440
12864
|
if (timeoutMs > 0) {
|
|
12441
12865
|
connectionTimeoutID = setTimeout(() => {
|