@iota-uz/sdk 0.4.24 → 0.4.26

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.
@@ -2,7 +2,7 @@ import React, { createContext, memo, useRef, useEffect, useCallback, useState, u
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 { CaretUp, CaretDown, DotsThreeVertical, Check, Copy, X, Columns, ArrowsIn, ArrowsOut, Warning, ArrowClockwise, Paperclip, PaperPlaneRight, ChartBar, FileText, Lightbulb, CircleNotch, ArrowUUpLeft, PencilSimple, Bookmark, ArrowsClockwise, Archive, Trash, UsersThree, DotsThree, Image, MagnifyingGlass, DownloadSimple, ArrowCounterClockwise, Bug, ArrowUp, ArrowDown, Stack, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, WarningCircle, FilePdf, FileXls, FileCsv, FileDoc, FileCode, File as File$1, MagnifyingGlassMinus, MagnifyingGlassPlus, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, ArrowSquareOut, Wrench, ClockCounterClockwise, Package, Plus, Crown, UserPlus, ArrowsCounterClockwise, ChatCircle, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, Table, SpinnerGap, FloppyDisk, ShareNetwork, Sidebar } from '@phosphor-icons/react';
5
+ import { CaretUp, CaretDown, DotsThreeVertical, Check, Copy, X, Columns, ArrowsIn, ArrowsOut, Warning, ArrowClockwise, Paperclip, Stop, PaperPlaneRight, ChartBar, FileText, Lightbulb, CircleNotch, ArrowUUpLeft, PencilSimple, Bookmark, ArrowsClockwise, Archive, Trash, UsersThree, DotsThree, Image, MagnifyingGlass, DownloadSimple, ArrowCounterClockwise, Bug, ArrowUp, ArrowDown, Stack, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, WarningCircle, FilePdf, FileXls, FileCsv, FileDoc, FileCode, File as File$1, MagnifyingGlassMinus, MagnifyingGlassPlus, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, ArrowSquareOut, Wrench, ClockCounterClockwise, Package, Plus, Crown, UserPlus, ArrowsCounterClockwise, ChatCircle, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, Table, SpinnerGap, FloppyDisk, ShareNetwork, 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';
@@ -1637,6 +1637,37 @@ function loadDebugMode(sessionId) {
1637
1637
  }
1638
1638
  }
1639
1639
 
1640
+ // ui/src/bichat/utils/reasoningEffortStorage.ts
1641
+ var STORAGE_KEY = "bichat.reasoningEffort";
1642
+ function saveReasoningEffort(effort) {
1643
+ if (typeof window === "undefined") {
1644
+ return;
1645
+ }
1646
+ try {
1647
+ window.sessionStorage.setItem(STORAGE_KEY, effort);
1648
+ } catch {
1649
+ }
1650
+ }
1651
+ function loadReasoningEffort() {
1652
+ if (typeof window === "undefined") {
1653
+ return null;
1654
+ }
1655
+ try {
1656
+ return window.sessionStorage.getItem(STORAGE_KEY);
1657
+ } catch {
1658
+ return null;
1659
+ }
1660
+ }
1661
+ function clearReasoningEffort() {
1662
+ if (typeof window === "undefined") {
1663
+ return;
1664
+ }
1665
+ try {
1666
+ window.sessionStorage.removeItem(STORAGE_KEY);
1667
+ } catch {
1668
+ }
1669
+ }
1670
+
1640
1671
  // ui/src/bichat/utils/debugTrace.ts
1641
1672
  function hasMeaningfulUsage(trace) {
1642
1673
  if (!trace) {
@@ -1779,6 +1810,16 @@ function readDebugLimitsFromGlobalContext() {
1779
1810
  completionReserveTokens
1780
1811
  };
1781
1812
  }
1813
+ function readReasoningEffortOptionsFromGlobalContext() {
1814
+ if (typeof window === "undefined") {
1815
+ return void 0;
1816
+ }
1817
+ const opts = window.__APPLET_CONTEXT__?.extensions?.llm?.reasoningEffortOptions;
1818
+ if (!Array.isArray(opts) || opts.length === 0) {
1819
+ return void 0;
1820
+ }
1821
+ return opts.filter((o) => typeof o === "string");
1822
+ }
1782
1823
 
1783
1824
  // ui/src/bichat/machine/selectors.ts
1784
1825
  function deriveDebugMode(state) {
@@ -1795,8 +1836,10 @@ function deriveSessionSnapshot(state, methods) {
1795
1836
  debugMode: deriveDebugMode(state),
1796
1837
  sessionDebugUsage: getSessionDebugUsage(state.messaging.turns),
1797
1838
  debugLimits: state.session.debugLimits,
1839
+ reasoningEffort: state.session.reasoningEffort,
1798
1840
  setError: methods.setError,
1799
- retryFetchSession: methods.retryFetchSession
1841
+ retryFetchSession: methods.retryFetchSession,
1842
+ setReasoningEffort: methods.setReasoningEffort
1800
1843
  };
1801
1844
  }
1802
1845
  function deriveMessagingSnapshot(state, methods) {
@@ -1986,6 +2029,7 @@ var ChatMachine = class {
1986
2029
  this.sendingSessionId = null;
1987
2030
  this.fetchCancelled = false;
1988
2031
  this.disposed = false;
2032
+ this.reasoningEffortOptions = null;
1989
2033
  /** Memoized sessionDebugUsage — avoids unnecessary session re-renders during streaming. */
1990
2034
  this.lastSessionDebugUsage = null;
1991
2035
  /** Interval handle for passive polling when another tab has an active stream. */
@@ -2017,7 +2061,8 @@ var ChatMachine = class {
2017
2061
  if (this.lastSessionSnapshotVersion !== this.sessionSnapshotVersion) {
2018
2062
  this.cachedSessionSnapshot = deriveSessionSnapshot(this.state, {
2019
2063
  setError: this.setError,
2020
- retryFetchSession: this.retryFetchSession
2064
+ retryFetchSession: this.retryFetchSession,
2065
+ setReasoningEffort: this.setReasoningEffort
2021
2066
  });
2022
2067
  this.lastSessionSnapshotVersion = this.sessionSnapshotVersion;
2023
2068
  }
@@ -2071,6 +2116,11 @@ var ChatMachine = class {
2071
2116
  this.dataSource = config.dataSource;
2072
2117
  this.rateLimiter = config.rateLimiter;
2073
2118
  this.onSessionCreated = config.onSessionCreated;
2119
+ this.reasoningEffortOptions = this.buildReasoningEffortOptions();
2120
+ const initialReasoningEffort = this.sanitizeReasoningEffort(loadReasoningEffort() || void 0);
2121
+ if (!initialReasoningEffort) {
2122
+ clearReasoningEffort();
2123
+ }
2074
2124
  this.state = {
2075
2125
  session: {
2076
2126
  currentSessionId: void 0,
@@ -2079,7 +2129,8 @@ var ChatMachine = class {
2079
2129
  error: null,
2080
2130
  errorRetryable: false,
2081
2131
  debugModeBySession: {},
2082
- debugLimits: readDebugLimitsFromGlobalContext()
2132
+ debugLimits: readDebugLimitsFromGlobalContext(),
2133
+ reasoningEffort: initialReasoningEffort
2083
2134
  },
2084
2135
  messaging: {
2085
2136
  turns: [],
@@ -2121,6 +2172,21 @@ var ChatMachine = class {
2121
2172
  this.enqueueMessage = this._enqueueMessage.bind(this);
2122
2173
  this.removeQueueItem = this._removeQueueItem.bind(this);
2123
2174
  this.updateQueueItem = this._updateQueueItem.bind(this);
2175
+ this.setReasoningEffort = this._setReasoningEffort.bind(this);
2176
+ }
2177
+ buildReasoningEffortOptions() {
2178
+ const options = readReasoningEffortOptionsFromGlobalContext();
2179
+ if (!options || options.length === 0) {
2180
+ return null;
2181
+ }
2182
+ return new Set(options);
2183
+ }
2184
+ // Keep outbound payloads constrained to server-declared options.
2185
+ sanitizeReasoningEffort(effort) {
2186
+ if (!effort || !this.reasoningEffortOptions || this.reasoningEffortOptions.size === 0) {
2187
+ return void 0;
2188
+ }
2189
+ return this.reasoningEffortOptions.has(effort) ? effort : void 0;
2124
2190
  }
2125
2191
  // =====================================================================
2126
2192
  // Lifecycle
@@ -2241,6 +2307,15 @@ var ChatMachine = class {
2241
2307
  }
2242
2308
  });
2243
2309
  }
2310
+ _setReasoningEffort(effort) {
2311
+ const next = this.sanitizeReasoningEffort(effort);
2312
+ this._updateSession({ reasoningEffort: next });
2313
+ if (next) {
2314
+ saveReasoningEffort(next);
2315
+ return;
2316
+ }
2317
+ clearReasoningEffort();
2318
+ }
2244
2319
  // =====================================================================
2245
2320
  // Private — session fetch
2246
2321
  // =====================================================================
@@ -2442,6 +2517,33 @@ var ChatMachine = class {
2442
2517
  });
2443
2518
  }
2444
2519
  }
2520
+ async _resumeAcceptedRunOrPoll(sessionId, runId) {
2521
+ setRunMarker(sessionId, runId);
2522
+ try {
2523
+ await this._runResumeStream(sessionId, runId);
2524
+ } catch (err) {
2525
+ if (this.disposed) {
2526
+ return;
2527
+ }
2528
+ console.warn("[ChatMachine] resumeStream failed, switching to status polling fallback:", err);
2529
+ const getStreamStatus2 = this.dataSource.getStreamStatus;
2530
+ const status = getStreamStatus2 ? await getStreamStatus2(sessionId).catch(() => null) : null;
2531
+ if (!status?.active) {
2532
+ clearRunMarker(sessionId);
2533
+ await this._syncSessionFromServer(sessionId, true).catch(() => {
2534
+ });
2535
+ this._updateMessaging({ generationInProgress: false });
2536
+ return;
2537
+ }
2538
+ setRunMarker(sessionId, status.runId ?? runId);
2539
+ this._updateMessaging({
2540
+ isStreaming: false,
2541
+ loading: false,
2542
+ generationInProgress: true
2543
+ });
2544
+ this._startPassivePolling(sessionId);
2545
+ }
2546
+ }
2445
2547
  // =====================================================================
2446
2548
  // Private — actions
2447
2549
  // =====================================================================
@@ -2560,20 +2662,28 @@ var ChatMachine = class {
2560
2662
  streamingContent: ""
2561
2663
  });
2562
2664
  try {
2563
- const compactResult = await this.dataSource.compactSessionHistory(curSessionId);
2564
- const summary = compactResult.summary || "";
2665
+ const accepted = await this.dataSource.compactSessionHistory(curSessionId);
2666
+ if (!accepted.runId) {
2667
+ throw new Error("Async compaction run metadata is missing");
2668
+ }
2565
2669
  this._updateMessaging({
2566
- turns: applyTurnLifecycleForPendingQuestion([createCompactedSystemTurn(curSessionId, summary)], null),
2670
+ turns: applyTurnLifecycleForPendingQuestion(
2671
+ [createCompactedSystemTurn(curSessionId, "Compacting conversation history...")],
2672
+ null
2673
+ ),
2567
2674
  pendingQuestion: null
2568
2675
  });
2569
- const result = await this.dataSource.fetchSession(curSessionId);
2570
- if (result) {
2571
- this._updateSession({ session: result.session });
2572
- this._setTurnsFromFetch(result.turns, result.pendingQuestion || null);
2573
- } else {
2574
- this._setTurnsFromFetch([], null);
2676
+ await this._resumeAcceptedRunOrPoll(curSessionId, accepted.runId);
2677
+ if (!this.state.messaging.generationInProgress) {
2678
+ const result = await this.dataSource.fetchSession(curSessionId);
2679
+ if (result) {
2680
+ this._updateSession({ session: result.session });
2681
+ this._setTurnsFromFetch(result.turns, result.pendingQuestion || null);
2682
+ } else {
2683
+ this._setTurnsFromFetch([], null);
2684
+ }
2685
+ this._updateMessaging({ codeOutputs: [] });
2575
2686
  }
2576
- this._updateMessaging({ codeOutputs: [] });
2577
2687
  } catch (err) {
2578
2688
  const normalized = normalizeRPCError(err, "Failed to compact session history");
2579
2689
  this._updateInput({ inputError: normalized.userMessage });
@@ -2633,6 +2743,7 @@ var ChatMachine = class {
2633
2743
  attachments,
2634
2744
  debugMode,
2635
2745
  replaceFromMessageID,
2746
+ reasoningEffort,
2636
2747
  tempTurnId
2637
2748
  } = params;
2638
2749
  let accumulatedContent = "";
@@ -2647,7 +2758,8 @@ var ChatMachine = class {
2647
2758
  this.abortController?.signal,
2648
2759
  {
2649
2760
  debugMode,
2650
- replaceFromMessageID
2761
+ replaceFromMessageID,
2762
+ reasoningEffort
2651
2763
  }
2652
2764
  )) {
2653
2765
  if (this.abortController?.signal.aborted) {
@@ -2738,8 +2850,6 @@ var ChatMachine = class {
2738
2850
  this._notifySessionsUpdated("session_created", targetSessionId);
2739
2851
  if (this.onSessionCreated) {
2740
2852
  this.onSessionCreated(targetSessionId);
2741
- } else {
2742
- this.dataSource.navigateToSession?.(targetSessionId);
2743
2853
  }
2744
2854
  }
2745
2855
  this._clearStreamError();
@@ -2846,6 +2956,7 @@ var ChatMachine = class {
2846
2956
  attachments,
2847
2957
  debugMode: curDebugMode,
2848
2958
  replaceFromMessageID,
2959
+ reasoningEffort: this.sanitizeReasoningEffort(this.state.session.reasoningEffort),
2849
2960
  tempTurnId: tempTurn.id
2850
2961
  });
2851
2962
  if (stopped) {
@@ -3025,9 +3136,24 @@ var ChatMachine = class {
3025
3136
  return;
3026
3137
  }
3027
3138
  if (result.success) {
3139
+ this._updateMessaging({
3140
+ pendingQuestion: null,
3141
+ turns: applyTurnLifecycleForPendingQuestion(this.state.messaging.turns, null)
3142
+ });
3028
3143
  if (result.data) {
3029
- this._updateSession({ session: result.data.session });
3030
- this._setTurnsFromFetch(result.data.turns, result.data.pendingQuestion || null);
3144
+ await this._resumeAcceptedRunOrPoll(curSessionId, result.data.runId);
3145
+ if (!this.state.messaging.generationInProgress) {
3146
+ const fetchResult = await this.dataSource.fetchSession(curSessionId);
3147
+ if (this.disposed) {
3148
+ return;
3149
+ }
3150
+ if (fetchResult) {
3151
+ this._updateSession({ session: fetchResult.session });
3152
+ this._setTurnsFromFetch(fetchResult.turns, fetchResult.pendingQuestion || null);
3153
+ } else {
3154
+ this._updateSession({ error: "Failed to load updated session", errorRetryable: true });
3155
+ }
3156
+ }
3031
3157
  } else if (curSessionId !== "new") {
3032
3158
  const fetchResult = await this.dataSource.fetchSession(curSessionId);
3033
3159
  if (this.disposed) {
@@ -3071,7 +3197,9 @@ var ChatMachine = class {
3071
3197
  pendingQuestion: null,
3072
3198
  turns: applyTurnLifecycleForPendingQuestion(this.state.messaging.turns, null)
3073
3199
  });
3074
- if (curSessionId !== "new") {
3200
+ if (result.data) {
3201
+ await this._resumeAcceptedRunOrPoll(curSessionId, result.data.runId);
3202
+ } else if (curSessionId !== "new") {
3075
3203
  const fetchResult = await this.dataSource.fetchSession(curSessionId);
3076
3204
  if (this.disposed) {
3077
3205
  return;
@@ -7065,6 +7193,43 @@ function CopyPill({ text }) {
7065
7193
  }
7066
7194
  );
7067
7195
  }
7196
+ function InlineCopyButton({ text }) {
7197
+ const [copied, setCopied] = useState(false);
7198
+ const timerRef = useRef(null);
7199
+ useEffect(() => () => {
7200
+ if (timerRef.current !== null) {
7201
+ clearTimeout(timerRef.current);
7202
+ }
7203
+ }, []);
7204
+ const handleCopy = async (e) => {
7205
+ e.stopPropagation();
7206
+ try {
7207
+ await navigator.clipboard.writeText(text);
7208
+ setCopied(true);
7209
+ if (timerRef.current !== null) {
7210
+ clearTimeout(timerRef.current);
7211
+ }
7212
+ timerRef.current = window.setTimeout(() => {
7213
+ setCopied(false);
7214
+ timerRef.current = null;
7215
+ }, 2e3);
7216
+ } catch (err) {
7217
+ console.error("Copy failed:", err);
7218
+ }
7219
+ };
7220
+ return /* @__PURE__ */ jsx(
7221
+ "button",
7222
+ {
7223
+ onClick: handleCopy,
7224
+ "aria-label": copied ? "Copied" : "Copy",
7225
+ className: [
7226
+ "flex-shrink-0 p-1 rounded transition-colors duration-150",
7227
+ copied ? "text-emerald-500 dark:text-emerald-400" : "text-gray-300 dark:text-gray-600 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700/40"
7228
+ ].join(" "),
7229
+ children: copied ? /* @__PURE__ */ jsx(Check, { size: 11, weight: "bold" }) : /* @__PURE__ */ jsx(Copy, { size: 11 })
7230
+ }
7231
+ );
7232
+ }
7068
7233
  function MetricChip({ icon, value, label }) {
7069
7234
  return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-gray-50 dark:bg-gray-800/40 text-[11px] tabular-nums", children: [
7070
7235
  icon,
@@ -7163,7 +7328,7 @@ function ToolCard({ tool }) {
7163
7328
  }
7164
7329
  );
7165
7330
  }
7166
- function DebugPanel({ trace, debugLimits = null }) {
7331
+ function DebugPanel({ trace }) {
7167
7332
  const hasData = !!trace && hasDebugTrace(trace);
7168
7333
  const traceID = trace?.traceId?.trim() || "";
7169
7334
  const traceURL = trace?.traceUrl?.trim() || "";
@@ -7182,22 +7347,6 @@ function DebugPanel({ trace, debugLimits = null }) {
7182
7347
  }
7183
7348
  })();
7184
7349
  const tokensPerSecond = calculateCompletionTokensPerSecond(trace?.usage, trace?.generationMs);
7185
- const effectiveMaxTokens = debugLimits?.effectiveMaxTokens ?? 0;
7186
- const promptTokens = trace?.usage?.promptTokens ?? 0;
7187
- const contextUsagePercent = calculateContextUsagePercent(promptTokens, effectiveMaxTokens);
7188
- const contextUsagePercentLabel = contextUsagePercent !== null ? contextUsagePercent.toFixed(1) : null;
7189
- const formatCompactTokens = (value) => {
7190
- if (!Number.isFinite(value) || value <= 0) {
7191
- return "0 tokens";
7192
- }
7193
- return `${new Intl.NumberFormat("en-US", {
7194
- notation: "compact",
7195
- maximumFractionDigits: value >= 1e5 ? 0 : 1
7196
- }).format(value)} tokens`;
7197
- };
7198
- const contextPercentValue = contextUsagePercent ?? 0;
7199
- const contextUsageToneClass = contextPercentValue > 75 ? "bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400" : contextPercentValue > 50 ? "bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400" : "bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400";
7200
- const contextUsageBarColor = contextPercentValue > 75 ? "#ef4444" : contextPercentValue > 50 ? "#f59e0b" : "#10b981";
7201
7350
  const metrics = [];
7202
7351
  if (hasData && trace) {
7203
7352
  if (trace.generationMs !== void 0) {
@@ -7250,34 +7399,36 @@ function DebugPanel({ trace, debugLimits = null }) {
7250
7399
  hasData && trace && /* @__PURE__ */ jsx(CopyPill, { text: JSON.stringify(trace, null, 2) })
7251
7400
  ] }),
7252
7401
  hasData && trace ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
7253
- (traceID || safeTraceURL) && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200/60 dark:border-gray-700/40 bg-gray-50/50 dark:bg-gray-800/40 p-3 space-y-2", children: [
7254
- traceID && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
7255
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
7256
- /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wider text-gray-500 dark:text-gray-400", children: "Trace ID" }),
7257
- /* @__PURE__ */ jsx("div", { className: "font-mono text-[11px] text-gray-800 dark:text-gray-200 break-all", children: traceID })
7258
- ] }),
7259
- /* @__PURE__ */ jsx(CopyPill, { text: traceID })
7402
+ (traceID || trace.sessionId) && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200/60 dark:border-gray-700/40 bg-gray-50/50 dark:bg-gray-800/40 px-3 py-2 space-y-1.5", children: [
7403
+ traceID && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
7404
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500 w-14", children: "Trace" }),
7405
+ /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 font-mono text-[11px] text-gray-700 dark:text-gray-300 truncate", title: traceID, children: traceID }),
7406
+ /* @__PURE__ */ jsx(InlineCopyButton, { text: traceID })
7260
7407
  ] }),
7261
- safeTraceURL && /* @__PURE__ */ jsxs(
7262
- "a",
7263
- {
7264
- href: safeTraceURL,
7265
- target: "_blank",
7266
- rel: "noopener noreferrer",
7267
- "aria-label": "View full trace in Langfuse (opens in new tab)",
7268
- className: "inline-flex items-center gap-1.5 text-[11px] font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
7269
- children: [
7270
- /* @__PURE__ */ jsx(ArrowSquareOut, { size: 12, weight: "bold" }),
7271
- /* @__PURE__ */ jsx("span", { children: "Open in Langfuse" })
7272
- ]
7273
- }
7274
- )
7275
- ] }),
7276
- (trace.thinking || trace.observationReason || trace.sessionId) && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200/60 dark:border-gray-700/40 bg-gray-50/50 dark:bg-gray-800/40 p-3 space-y-2", children: [
7277
- trace.sessionId && /* @__PURE__ */ jsxs("div", { className: "text-[10px] uppercase tracking-wider text-gray-500 dark:text-gray-400", children: [
7278
- "Session: ",
7279
- /* @__PURE__ */ jsx("span", { className: "font-mono normal-case break-all", children: trace.sessionId })
7408
+ trace.sessionId && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
7409
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500 w-14", children: "Session" }),
7410
+ /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 font-mono text-[11px] text-gray-700 dark:text-gray-300 truncate", title: trace.sessionId, children: trace.sessionId }),
7411
+ /* @__PURE__ */ jsx(InlineCopyButton, { text: trace.sessionId })
7280
7412
  ] }),
7413
+ safeTraceURL && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 pt-0.5", children: [
7414
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 w-14" }),
7415
+ /* @__PURE__ */ jsxs(
7416
+ "a",
7417
+ {
7418
+ href: safeTraceURL,
7419
+ target: "_blank",
7420
+ rel: "noopener noreferrer",
7421
+ "aria-label": "Open in Langfuse",
7422
+ className: "inline-flex items-center gap-1.5 text-[11px] font-medium text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors duration-150",
7423
+ children: [
7424
+ /* @__PURE__ */ jsx(ArrowSquareOut, { size: 11, weight: "bold" }),
7425
+ /* @__PURE__ */ jsx("span", { children: "Open in Langfuse" })
7426
+ ]
7427
+ }
7428
+ )
7429
+ ] })
7430
+ ] }),
7431
+ (trace.thinking || trace.observationReason) && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200/60 dark:border-gray-700/40 bg-gray-50/50 dark:bg-gray-800/40 p-3 space-y-2", children: [
7281
7432
  trace.observationReason && /* @__PURE__ */ jsxs("div", { className: "text-[11px] text-amber-700 dark:text-amber-300", children: [
7282
7433
  "Observation: ",
7283
7434
  /* @__PURE__ */ jsx("span", { className: "font-mono", children: trace.observationReason })
@@ -7317,30 +7468,6 @@ function DebugPanel({ trace, debugLimits = null }) {
7317
7468
  /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 rounded-full bg-gray-100 dark:bg-gray-800 text-[10px] font-mono font-medium text-gray-500 dark:text-gray-400 tabular-nums", children: trace.tools.length })
7318
7469
  ] }),
7319
7470
  /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: trace.tools.map((tool, idx) => /* @__PURE__ */ jsx(ToolCard, { tool }, `${tool.callId || tool.name}-${idx}`)) })
7320
- ] }),
7321
- contextUsagePercentLabel !== null && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200/60 dark:border-gray-700/40 bg-gray-50/50 dark:bg-gray-800/40 p-3 space-y-2", children: [
7322
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
7323
- /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wider text-gray-500 dark:text-gray-400", children: "Context usage" }),
7324
- /* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] text-gray-500 dark:text-gray-400 tabular-nums", children: [
7325
- formatCompactTokens(promptTokens),
7326
- " / ",
7327
- formatCompactTokens(effectiveMaxTokens)
7328
- ] }),
7329
- /* @__PURE__ */ jsxs("span", { className: `px-1.5 py-0.5 rounded-full text-[10px] font-semibold tabular-nums ${contextUsageToneClass}`, children: [
7330
- contextUsagePercentLabel,
7331
- "%"
7332
- ] })
7333
- ] }),
7334
- /* @__PURE__ */ jsx("div", { className: "h-1.5 rounded-full bg-gray-200/80 dark:bg-gray-700/50 overflow-hidden", children: /* @__PURE__ */ jsx(
7335
- "div",
7336
- {
7337
- className: "h-full rounded-full transition-all duration-700 ease-out",
7338
- style: {
7339
- width: `${Math.min(contextPercentValue, 100)}%`,
7340
- backgroundColor: contextUsageBarColor
7341
- }
7342
- }
7343
- ) })
7344
7471
  ] })
7345
7472
  ] }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 dark:text-gray-500 italic", children: "Debug info unavailable" })
7346
7473
  ] });
@@ -7402,8 +7529,7 @@ function AssistantMessage({
7402
7529
  hideAvatar = false,
7403
7530
  hideActions = false,
7404
7531
  hideTimestamp = false,
7405
- showDebug = false,
7406
- debugLimits = null
7532
+ showDebug = false
7407
7533
  }) {
7408
7534
  const { t } = useTranslation();
7409
7535
  const [explanationExpanded, setExplanationExpanded] = useState(false);
@@ -7594,7 +7720,7 @@ function AssistantMessage({
7594
7720
  explanationExpanded && /* @__PURE__ */ jsx("div", { className: "pt-3 text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { children: t("BiChat.Common.Loading") }), children: /* @__PURE__ */ jsx(MarkdownRenderer2, { content: turn.explanation }) }) })
7595
7721
  ] })
7596
7722
  ) }),
7597
- showDebug && /* @__PURE__ */ jsx(DebugPanel, { trace: turn.debug, debugLimits })
7723
+ showDebug && /* @__PURE__ */ jsx(DebugPanel, { trace: turn.debug })
7598
7724
  ] }),
7599
7725
  turn.artifacts && turn.artifacts.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.artifacts, children: renderSlot(
7600
7726
  slots?.artifacts,
@@ -7786,7 +7912,7 @@ function AssistantTurnView({
7786
7912
  hideTimestamp,
7787
7913
  allowRegenerate = true
7788
7914
  }) {
7789
- const { debugMode, debugLimits } = useChatSession();
7915
+ const { debugMode } = useChatSession();
7790
7916
  const { handleCopy, handleRegenerate, pendingQuestion, sendMessage: sendMessage2, loading } = useChatMessaging();
7791
7917
  const assistantTurn = turn.assistantTurn;
7792
7918
  if (!assistantTurn) {
@@ -7821,8 +7947,7 @@ function AssistantTurnView({
7821
7947
  hideAvatar,
7822
7948
  hideActions,
7823
7949
  hideTimestamp,
7824
- showDebug: debugMode,
7825
- debugLimits
7950
+ showDebug: debugMode
7826
7951
  }
7827
7952
  );
7828
7953
  }
@@ -8967,6 +9092,36 @@ function DebugStatsPanel({ debugSessionUsage, debugLimits }) {
8967
9092
  ] })
8968
9093
  ] });
8969
9094
  }
9095
+ var EFFORT_LABEL_KEYS = {
9096
+ low: "BiChat.Input.ReasoningEffortLow",
9097
+ medium: "BiChat.Input.ReasoningEffortMedium",
9098
+ high: "BiChat.Input.ReasoningEffortHigh",
9099
+ xhigh: "BiChat.Input.ReasoningEffortXHigh"
9100
+ };
9101
+ function ReasoningEffortSelector({ options, value, onChange, disabled }) {
9102
+ const { t } = useTranslation();
9103
+ const selected = value || options[1] || options[0];
9104
+ const label = t("BiChat.Input.ReasoningEffort");
9105
+ return /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 self-center flex items-center gap-1.5", children: [
9106
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400 dark:text-gray-500 font-medium whitespace-nowrap select-none", children: label }),
9107
+ /* @__PURE__ */ jsx(
9108
+ "select",
9109
+ {
9110
+ value: selected,
9111
+ disabled,
9112
+ onChange: (event) => onChange(event.target.value),
9113
+ className: [
9114
+ "cursor-pointer h-8 rounded-lg border border-gray-200 dark:border-gray-600",
9115
+ "bg-gray-50 dark:bg-gray-700/50 px-2.5 text-[11px] font-medium leading-none",
9116
+ "text-gray-700 dark:text-gray-200 focus:outline-none focus:ring-2 focus:ring-primary-500/25",
9117
+ "disabled:opacity-40 disabled:cursor-not-allowed"
9118
+ ].join(" "),
9119
+ "aria-label": label,
9120
+ children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: t(EFFORT_LABEL_KEYS[opt] ?? opt) }, opt))
9121
+ }
9122
+ )
9123
+ ] });
9124
+ }
8970
9125
  var MAX_FILES_DEFAULT = 10;
8971
9126
  var MAX_FILE_SIZE_DEFAULT = 20 * 1024 * 1024;
8972
9127
  var MAX_HEIGHT = 192;
@@ -8993,7 +9148,10 @@ var MessageInput = forwardRef(
8993
9148
  maxFiles = MAX_FILES_DEFAULT,
8994
9149
  maxFileSize = MAX_FILE_SIZE_DEFAULT,
8995
9150
  containerClassName,
8996
- formClassName
9151
+ formClassName,
9152
+ reasoningEffortOptions,
9153
+ reasoningEffort,
9154
+ onReasoningEffortChange
8997
9155
  }, ref) => {
8998
9156
  const { t } = useTranslation();
8999
9157
  const [attachments, setAttachments] = useState([]);
@@ -9442,16 +9600,25 @@ var MessageInput = forwardRef(
9442
9600
  "aria-label": t("BiChat.Input.MessageInput")
9443
9601
  }
9444
9602
  ) }),
9603
+ reasoningEffortOptions && reasoningEffortOptions.length > 0 && onReasoningEffortChange && /* @__PURE__ */ jsx(
9604
+ ReasoningEffortSelector,
9605
+ {
9606
+ options: reasoningEffortOptions,
9607
+ value: reasoningEffort,
9608
+ onChange: onReasoningEffortChange,
9609
+ disabled: disabled || loading
9610
+ }
9611
+ ),
9445
9612
  isStreaming && onCancelStreaming ? /* @__PURE__ */ jsx(
9446
9613
  "button",
9447
9614
  {
9448
9615
  type: "button",
9449
9616
  onClick: onCancelStreaming,
9450
9617
  disabled: disabled || fetching,
9451
- className: "cursor-pointer flex-shrink-0 self-center p-2 rounded-lg bg-red-600 hover:bg-red-700 active:bg-red-800 active:scale-95 text-white shadow-sm transition-all disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-red-600",
9618
+ className: "cursor-pointer flex-shrink-0 self-center p-2 rounded-lg bg-gray-900 hover:bg-gray-800 active:bg-black active:scale-95 text-white shadow-sm transition-all dark:bg-gray-100 dark:hover:bg-gray-200 dark:active:bg-white dark:text-gray-900 disabled:opacity-40 disabled:cursor-not-allowed",
9452
9619
  "aria-label": t("BiChat.Common.Cancel"),
9453
9620
  title: t("BiChat.Common.Cancel"),
9454
- children: /* @__PURE__ */ jsx(X, { size: 18, weight: "bold" })
9621
+ children: /* @__PURE__ */ jsx(Stop, { size: 18, weight: "fill" })
9455
9622
  }
9456
9623
  ) : /* @__PURE__ */ jsx(
9457
9624
  "button",
@@ -11641,7 +11808,9 @@ function ChatSessionCore({
11641
11808
  debugLimits,
11642
11809
  currentSessionId,
11643
11810
  setError,
11644
- retryFetchSession
11811
+ retryFetchSession,
11812
+ reasoningEffort,
11813
+ setReasoningEffort
11645
11814
  } = useChatSession();
11646
11815
  const {
11647
11816
  turns,
@@ -11669,6 +11838,7 @@ function ChatSessionCore({
11669
11838
  const accessReadOnly = session?.access ? !session.access.canWrite : false;
11670
11839
  const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived || accessReadOnly;
11671
11840
  const [restoring, setRestoring] = useState(false);
11841
+ const [reasoningEffortOptions] = useState(() => readReasoningEffortOptionsFromGlobalContext());
11672
11842
  const handleRestore = useCallback(async () => {
11673
11843
  if (!session?.id) {
11674
11844
  return;
@@ -11936,7 +12106,10 @@ function ChatSessionCore({
11936
12106
  onUpdateQueueItem: updateQueueItem,
11937
12107
  onCancelStreaming: cancel,
11938
12108
  containerClassName: "pt-6 px-6",
11939
- formClassName: "mx-auto"
12109
+ formClassName: "mx-auto",
12110
+ reasoningEffortOptions,
12111
+ reasoningEffort,
12112
+ onReasoningEffortChange: setReasoningEffort
11940
12113
  }
11941
12114
  ),
11942
12115
  /* @__PURE__ */ jsx("p", { className: "mt-4 pb-1 text-center text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Welcome.Disclaimer") })
@@ -11992,7 +12165,10 @@ function ChatSessionCore({
11992
12165
  onUnqueue: handleUnqueue,
11993
12166
  onRemoveQueueItem: removeQueueItem,
11994
12167
  onUpdateQueueItem: updateQueueItem,
11995
- onCancelStreaming: cancel
12168
+ onCancelStreaming: cancel,
12169
+ reasoningEffortOptions,
12170
+ reasoningEffort,
12171
+ onReasoningEffortChange: setReasoningEffort
11996
12172
  }
11997
12173
  )
11998
12174
  ] }) }),
@@ -16880,9 +17056,10 @@ function useHttpDataSourceConfigFromApplet(options) {
16880
17056
  rpcEndpoint,
16881
17057
  streamEndpoint,
16882
17058
  csrfToken,
16883
- timeout: options?.timeout ?? 12e4
17059
+ rpcTimeoutMs: options?.rpcTimeoutMs ?? 12e4,
17060
+ streamConnectTimeoutMs: options?.streamConnectTimeoutMs ?? 3e4
16884
17061
  };
16885
- }, [options?.timeout]);
17062
+ }, [options?.rpcTimeoutMs, options?.streamConnectTimeoutMs]);
16886
17063
  }
16887
17064
  var SESSION_PATH_REGEX = /\/session\/([^/]+)/;
16888
17065
  function useBichatRouter({
@@ -18048,7 +18225,23 @@ async function clearSessionHistory(callRPC, sessionId) {
18048
18225
  return callRPC("bichat.session.clear", { id: sessionId });
18049
18226
  }
18050
18227
  async function compactSessionHistory(callRPC, sessionId) {
18051
- return callRPC("bichat.session.compact", { id: sessionId });
18228
+ const result = await callRPC("bichat.session.compact", { id: sessionId });
18229
+ if (!result.accepted) {
18230
+ throw new Error("Session compact request was not accepted");
18231
+ }
18232
+ if (result.operation !== "session_compact") {
18233
+ throw new Error(`Unexpected async operation: ${result.operation}`);
18234
+ }
18235
+ if (!result.sessionId || !result.runId) {
18236
+ throw new Error("Missing async run metadata");
18237
+ }
18238
+ return {
18239
+ accepted: true,
18240
+ operation: result.operation,
18241
+ sessionId: result.sessionId,
18242
+ runId: result.runId,
18243
+ startedAt: result.startedAt
18244
+ };
18052
18245
  }
18053
18246
  async function listUsers(callRPC) {
18054
18247
  const data = await callRPC("bichat.user.list", {});
@@ -18167,6 +18360,9 @@ var TERMINAL_TYPES = /* @__PURE__ */ new Set(["done", "error"]);
18167
18360
  async function* parseBichatStream(reader) {
18168
18361
  let yieldedTerminal = false;
18169
18362
  for await (const event of parseSSEStream(reader)) {
18363
+ if (event.type === "ping") {
18364
+ continue;
18365
+ }
18170
18366
  const parsed = event;
18171
18367
  const inferredType = parsed.type || (parsed.content ? "content" : "error");
18172
18368
  const normalized = {
@@ -18504,7 +18700,10 @@ async function* sendMessage(deps, sessionId, content, attachments = [], signal,
18504
18700
  replaceFromMessageId: options?.replaceFromMessageID,
18505
18701
  attachments: streamAttachments
18506
18702
  };
18507
- const timeoutMs = deps.timeout ?? 0;
18703
+ if (options?.reasoningEffort) {
18704
+ payload.reasoningEffort = options.reasoningEffort;
18705
+ }
18706
+ const timeoutMs = deps.streamConnectTimeoutMs ?? 0;
18508
18707
  if (timeoutMs > 0) {
18509
18708
  connectionTimeoutID = setTimeout(() => {
18510
18709
  connectionTimedOut = true;
@@ -18539,7 +18738,7 @@ async function* sendMessage(deps, sessionId, content, attachments = [], signal,
18539
18738
  if (err.name === "AbortError") {
18540
18739
  yield {
18541
18740
  type: "error",
18542
- error: connectionTimedOut ? `Stream request timed out after ${deps.timeout}ms` : "Stream cancelled"
18741
+ error: connectionTimedOut ? `Stream request timed out after ${deps.streamConnectTimeoutMs}ms` : "Stream cancelled"
18543
18742
  };
18544
18743
  } else {
18545
18744
  yield {
@@ -18627,7 +18826,7 @@ async function resumeStream(deps, sessionId, runId, onChunk, signal) {
18627
18826
  const url = buildStreamUrl(deps, "/resume");
18628
18827
  const controller = new AbortController();
18629
18828
  let timeoutId;
18630
- const timeoutMs = deps.timeout;
18829
+ const timeoutMs = deps.connectTimeoutMs;
18631
18830
  if (timeoutMs != null && timeoutMs > 0) {
18632
18831
  timeoutId = setTimeout(() => controller.abort(), timeoutMs);
18633
18832
  }
@@ -18644,6 +18843,10 @@ async function resumeStream(deps, sessionId, runId, onChunk, signal) {
18644
18843
  if (!response.ok) {
18645
18844
  throw new Error(`Resume stream failed: HTTP ${response.status}`);
18646
18845
  }
18846
+ if (timeoutId !== void 0) {
18847
+ clearTimeout(timeoutId);
18848
+ timeoutId = void 0;
18849
+ }
18647
18850
  if (!response.body) {
18648
18851
  throw new Error("Resume response body is null");
18649
18852
  }
@@ -18677,11 +18880,7 @@ async function submitQuestionAnswers(callRPC, sessionId, questionId, answers) {
18677
18880
  });
18678
18881
  return {
18679
18882
  success: true,
18680
- data: {
18681
- session: toSession(result.session),
18682
- turns: normalizeTurns(sanitizeConversationTurns(result.turns, sessionId)),
18683
- pendingQuestion: sanitizePendingQuestion(result.pendingQuestion, sessionId)
18684
- }
18883
+ data: normalizeAsyncRunAccepted(result)
18685
18884
  };
18686
18885
  } catch (err) {
18687
18886
  return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
@@ -18689,12 +18888,33 @@ async function submitQuestionAnswers(callRPC, sessionId, questionId, answers) {
18689
18888
  }
18690
18889
  async function rejectPendingQuestion(callRPC, sessionId) {
18691
18890
  try {
18692
- await callRPC("bichat.question.reject", { sessionId });
18693
- return { success: true };
18891
+ const result = await callRPC("bichat.question.reject", { sessionId });
18892
+ return { success: true, data: normalizeAsyncRunAccepted(result) };
18694
18893
  } catch (err) {
18695
18894
  return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
18696
18895
  }
18697
18896
  }
18897
+ function isAsyncRunOperation(value) {
18898
+ return value === "question_submit" || value === "question_reject" || value === "session_compact";
18899
+ }
18900
+ function normalizeAsyncRunAccepted(input) {
18901
+ if (!input.accepted) {
18902
+ throw new Error("Async run request was not accepted");
18903
+ }
18904
+ if (!isAsyncRunOperation(input.operation)) {
18905
+ throw new Error(`Unexpected async operation: ${input.operation}`);
18906
+ }
18907
+ if (!input.sessionId || !input.runId) {
18908
+ throw new Error("Missing async run metadata");
18909
+ }
18910
+ return {
18911
+ accepted: true,
18912
+ operation: input.operation,
18913
+ sessionId: input.sessionId,
18914
+ runId: input.runId,
18915
+ startedAt: input.startedAt
18916
+ };
18917
+ }
18698
18918
 
18699
18919
  // ui/src/bichat/data/ArtifactManager.ts
18700
18920
  async function fetchSessionArtifacts(callRPC, sessionId, options) {
@@ -18766,15 +18986,13 @@ var HttpDataSource = class {
18766
18986
  this.config = {
18767
18987
  streamEndpoint: "/stream",
18768
18988
  uploadEndpoint: "/api/uploads",
18769
- timeout: 12e4,
18770
- ...config
18989
+ ...config,
18990
+ rpcTimeoutMs: typeof config.rpcTimeoutMs === "number" ? config.rpcTimeoutMs : 12e4,
18991
+ streamConnectTimeoutMs: typeof config.streamConnectTimeoutMs === "number" ? config.streamConnectTimeoutMs : 3e4
18771
18992
  };
18772
- if (config.navigateToSession) {
18773
- this.navigateToSession = config.navigateToSession;
18774
- }
18775
18993
  this.rpc = createAppletRPCClient({
18776
18994
  endpoint: `${this.config.baseUrl}${this.config.rpcEndpoint}`,
18777
- timeoutMs: this.config.timeout
18995
+ timeoutMs: this.config.rpcTimeoutMs
18778
18996
  });
18779
18997
  }
18780
18998
  // -------------------------------------------------------------------------
@@ -18894,7 +19112,7 @@ var HttpDataSource = class {
18894
19112
  baseUrl: this.config.baseUrl,
18895
19113
  streamEndpoint: this.config.streamEndpoint,
18896
19114
  createHeaders: (h) => this.createHeaders(h),
18897
- timeoutMs: this.config.timeout
19115
+ timeoutMs: this.config.rpcTimeoutMs
18898
19116
  },
18899
19117
  sessionId
18900
19118
  );
@@ -18905,7 +19123,7 @@ var HttpDataSource = class {
18905
19123
  baseUrl: this.config.baseUrl,
18906
19124
  streamEndpoint: this.config.streamEndpoint,
18907
19125
  createHeaders: (h) => this.createHeaders(h),
18908
- timeout: this.config.timeout
19126
+ connectTimeoutMs: this.config.streamConnectTimeoutMs
18909
19127
  },
18910
19128
  sessionId,
18911
19129
  runId,
@@ -18929,7 +19147,8 @@ var HttpDataSource = class {
18929
19147
  callRPC: this.boundCallRPC,
18930
19148
  baseUrl: this.config.baseUrl,
18931
19149
  streamEndpoint: this.config.streamEndpoint,
18932
- timeout: this.config.timeout,
19150
+ rpcTimeoutMs: this.config.rpcTimeoutMs,
19151
+ streamConnectTimeoutMs: this.config.streamConnectTimeoutMs,
18933
19152
  createHeaders: (additional) => this.createHeaders(additional),
18934
19153
  uploadFileFn: this.boundUploadFile,
18935
19154
  logAttachmentLifecycle: () => {
@@ -18980,17 +19199,6 @@ var HttpDataSource = class {
18980
19199
  async deleteSessionArtifact(artifactId) {
18981
19200
  return deleteSessionArtifact(this.boundCallRPC, artifactId);
18982
19201
  }
18983
- // -------------------------------------------------------------------------
18984
- // Navigation (optional, deprecated)
18985
- // -------------------------------------------------------------------------
18986
- /**
18987
- * @deprecated Pass `onSessionCreated` to `ChatSessionProvider` instead.
18988
- */
18989
- navigateToSession(sessionId) {
18990
- if (typeof window !== "undefined") {
18991
- window.location.href = `/chat/${sessionId}`;
18992
- }
18993
- }
18994
19202
  };
18995
19203
  function createHttpDataSource(config) {
18996
19204
  return new HttpDataSource(config);