@flutchai/flutch-sdk 0.7.10 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2133,11 +2133,28 @@ exports.GraphController = class _GraphController {
2133
2133
  }
2134
2134
  async generateAnswer(payload, res) {
2135
2135
  this.cancelOnDisconnect(res, payload.requestId);
2136
+ let beating = false;
2137
+ const stop = this.heartbeat(() => {
2138
+ if (!beating) {
2139
+ res.status(common.HttpStatus.CREATED).setHeader("Content-Type", "application/json");
2140
+ beating = true;
2141
+ }
2142
+ res.write(" ");
2143
+ });
2136
2144
  try {
2137
- return await this.graphService.generateAnswer(payload);
2145
+ const result = await this.graphService.generateAnswer(payload);
2146
+ stop();
2147
+ if (beating) res.end(JSON.stringify(result));
2148
+ else res.status(common.HttpStatus.CREATED).json(result);
2138
2149
  } catch (error) {
2150
+ stop();
2139
2151
  this.logger.error(`Generation failed: ${error.message}`);
2140
- throw new common.HttpException(`Generation failed: ${error.message}`, common.HttpStatus.INTERNAL_SERVER_ERROR);
2152
+ const failure = {
2153
+ statusCode: common.HttpStatus.INTERNAL_SERVER_ERROR,
2154
+ message: `Generation failed: ${error.message}`
2155
+ };
2156
+ if (beating) res.end(JSON.stringify(failure));
2157
+ else res.status(common.HttpStatus.INTERNAL_SERVER_ERROR).json(failure);
2141
2158
  }
2142
2159
  }
2143
2160
  async streamAnswer(payload, res) {
@@ -2146,6 +2163,7 @@ exports.GraphController = class _GraphController {
2146
2163
  res.setHeader("Cache-Control", "no-cache");
2147
2164
  res.setHeader("Connection", "keep-alive");
2148
2165
  this.cancelOnDisconnect(res, payload.requestId);
2166
+ const stop = this.heartbeat(() => res.write(": ping\n\n"));
2149
2167
  try {
2150
2168
  this.logger.debug(`[CONTROLLER] Starting streamAnswer for requestId: ${payload.requestId}`);
2151
2169
  const result = await this.graphService.streamAnswer(payload, (chunk) => {
@@ -2163,6 +2181,7 @@ exports.GraphController = class _GraphController {
2163
2181
  reasoningChainsCount: result.reasoningChains?.length || 0,
2164
2182
  resultKeys: Object.keys(result || {})
2165
2183
  });
2184
+ stop();
2166
2185
  res.write(`event: final
2167
2186
  `);
2168
2187
  res.write(`data: ${JSON.stringify(result)}
@@ -2171,6 +2190,7 @@ exports.GraphController = class _GraphController {
2171
2190
  res.end();
2172
2191
  this.logger.debug(`[CONTROLLER] Stream completed successfully`);
2173
2192
  } catch (error) {
2193
+ stop();
2174
2194
  this.logger.error(`[CONTROLLER] Streaming failed: ${error.message}`);
2175
2195
  this.logger.error(`[CONTROLLER] Error stack: ${error.stack}`);
2176
2196
  res.write(`event: error
@@ -2184,6 +2204,20 @@ exports.GraphController = class _GraphController {
2184
2204
  }
2185
2205
  }
2186
2206
  /**
2207
+ * Calls `beat` every GRAPH_HEARTBEAT_MS (20 s by default, 0 turns it off)
2208
+ * while a run goes on; returns the function that stops it. A caller drops a
2209
+ * connection that stays silent past its idle limit — the backend's is
2210
+ * GRAPH_SERVICE_TIMEOUT_MS — and a graph waiting on other agents is silent
2211
+ * for minutes: a report pipeline was cut at 15 minutes, 21 seconds before
2212
+ * its report was done (29.10.2026).
2213
+ */
2214
+ heartbeat(beat) {
2215
+ const ms = Number(process.env.GRAPH_HEARTBEAT_MS ?? 2e4);
2216
+ if (!(ms > 0)) return () => void 0;
2217
+ const timer = setInterval(beat, ms);
2218
+ return () => clearInterval(timer);
2219
+ }
2220
+ /**
2187
2221
  * A caller that hangs up before the answer is done — its own timeout, a
2188
2222
  * restart, a cancelled parent run — no longer waits for it, so the run is
2189
2223
  * cancelled: left going, it keeps calling models and tools nobody reads.
@@ -2269,9 +2303,7 @@ _ts_decorate8([
2269
2303
  description: "Answer generated"
2270
2304
  }),
2271
2305
  _ts_param4(0, common.Body()),
2272
- _ts_param4(1, common.Res({
2273
- passthrough: true
2274
- })),
2306
+ _ts_param4(1, common.Res()),
2275
2307
  _ts_metadata6("design:type", Function),
2276
2308
  _ts_metadata6("design:paramtypes", [
2277
2309
  typeof IGraphRequestPayload === "undefined" ? Object : IGraphRequestPayload,
@@ -4806,6 +4838,20 @@ var StreamChannel = /* @__PURE__ */ (function(StreamChannel2) {
4806
4838
  return StreamChannel2;
4807
4839
  })({});
4808
4840
 
4841
+ // src/messages/thinking.ts
4842
+ var textOf = /* @__PURE__ */ __name((part) => typeof part === "string" ? part : part && typeof part.text === "string" ? String(part.text) : "", "textOf");
4843
+ var summaryTexts = /* @__PURE__ */ __name((summary) => Array.isArray(summary) ? summary.map(textOf) : [], "summaryTexts");
4844
+ function extractThinking(message) {
4845
+ const blocks = Array.isArray(message.content) ? message.content : [];
4846
+ const reasoning = message.additional_kwargs?.reasoning;
4847
+ return [
4848
+ ...blocks.filter((b) => b?.type === "thinking" && typeof b.thinking === "string").map((b) => String(b.thinking)),
4849
+ ...blocks.filter((b) => b?.type === "reasoning").flatMap((b) => summaryTexts(b.summary)),
4850
+ ...summaryTexts(reasoning?.summary)
4851
+ ].map((t) => t.trim()).filter(Boolean).filter((t, i, all) => all.indexOf(t) === i).join("\n\n");
4852
+ }
4853
+ __name(extractThinking, "extractThinking");
4854
+
4809
4855
  // src/tools/attachment-summary.ts
4810
4856
  var MAX_SAMPLE_ROWS = 5;
4811
4857
  var MAX_TEXT_PREVIEW_LENGTH = 500;
@@ -5156,6 +5202,45 @@ function sanitizeTraceError(error, options) {
5156
5202
  };
5157
5203
  }
5158
5204
  __name(sanitizeTraceError, "sanitizeTraceError");
5205
+
5206
+ // src/engines/langgraph/stream-thinking.ts
5207
+ function splitThinking(blocks) {
5208
+ const thoughts = [];
5209
+ const rest = [];
5210
+ for (const block of blocks) {
5211
+ const part = block;
5212
+ if (part?.type === "thinking" || part?.type === "reasoning") {
5213
+ const text = part.type === "thinking" ? part.thinking : part.reasoning;
5214
+ if (typeof text === "string" && text) thoughts.push({
5215
+ text,
5216
+ index: part.index
5217
+ });
5218
+ continue;
5219
+ }
5220
+ rest.push(block);
5221
+ }
5222
+ return {
5223
+ thoughts,
5224
+ rest
5225
+ };
5226
+ }
5227
+ __name(splitThinking, "splitThinking");
5228
+ function thinkingTexts(state, runId, deltas) {
5229
+ const out = [];
5230
+ for (const delta of deltas) {
5231
+ const another = runId !== state.runId || state.index !== void 0 && delta.index !== state.index;
5232
+ out.push(state.shown && another ? `
5233
+
5234
+ ${delta.text}` : delta.text);
5235
+ state.runId = runId;
5236
+ state.index = delta.index;
5237
+ state.shown = true;
5238
+ }
5239
+ return out;
5240
+ }
5241
+ __name(thinkingTexts, "thinkingTexts");
5242
+
5243
+ // src/engines/langgraph/event-processor.utils.ts
5159
5244
  function _ts_decorate21(decorators, target, key, desc) {
5160
5245
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5161
5246
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -5463,7 +5548,21 @@ exports.EventProcessor = class _EventProcessor {
5463
5548
  const chunk = event.data.chunk;
5464
5549
  const blocks = [];
5465
5550
  if (chunk.content) {
5466
- blocks.push(...this.normalizeContentBlocks(chunk.content));
5551
+ const { thoughts, rest } = splitThinking(this.normalizeContentBlocks(chunk.content));
5552
+ blocks.push(...rest);
5553
+ if (thoughts.length && event.metadata?.stream_thinking === true) {
5554
+ acc.thinking ??= {
5555
+ runId: null,
5556
+ index: void 0,
5557
+ shown: false
5558
+ };
5559
+ for (const text of thinkingTexts(acc.thinking, event.run_id ?? null, thoughts)) this.processContentStream(acc, StreamChannel.PROCESSING, [
5560
+ {
5561
+ type: "text",
5562
+ text
5563
+ }
5564
+ ], onPartial);
5565
+ }
5467
5566
  }
5468
5567
  const contentHasToolBlocks = blocks.some((block) => block?.type === "tool_use" || block?.type === "tool_call" || block?.type === "input_json_delta");
5469
5568
  if (!contentHasToolBlocks && Array.isArray(chunk.tool_call_chunks) && chunk.tool_call_chunks.length > 0) {
@@ -9275,6 +9374,7 @@ exports.decryptTokens = decryptTokens;
9275
9374
  exports.dispatchAttachments = dispatchAttachments;
9276
9375
  exports.encryptTokens = encryptTokens;
9277
9376
  exports.executeToolWithAttachments = executeToolWithAttachments;
9377
+ exports.extractThinking = extractThinking;
9278
9378
  exports.findCallbackMethod = findCallbackMethod;
9279
9379
  exports.findEndpointMethod = findEndpointMethod;
9280
9380
  exports.flutchFetch = flutchFetch;