openfox 1.6.20 → 1.6.23

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.
Files changed (35) hide show
  1. package/README.md +46 -13
  2. package/dist/{auto-compaction-NFLWKJAO.js → auto-compaction-7LK3QGPI.js} +4 -3
  3. package/dist/{chat-handler-2RVTVNPF.js → chat-handler-ZU74RXED.js} +6 -5
  4. package/dist/{chunk-KOBGCNUE.js → chunk-4O3C2JMB.js} +58 -14
  5. package/dist/{chunk-QY7BMXWT.js → chunk-B7E3BICY.js} +6 -2
  6. package/dist/{chunk-U3KMU3UE.js → chunk-BOEXJCOD.js} +2 -2
  7. package/dist/{chunk-IN5EP4ZB.js → chunk-CJNQGEYG.js} +2 -2
  8. package/dist/{chunk-OVLFEBRR.js → chunk-D4ZLSV6P.js} +133 -468
  9. package/dist/{chunk-LTPZ4GTW.js → chunk-FTJPNCAV.js} +6 -6
  10. package/dist/chunk-KSASIV4B.js +486 -0
  11. package/dist/{chunk-ZDNXCVW4.js → chunk-P6GUT2QQ.js} +11 -5
  12. package/dist/{chunk-KOUMYBYM.js → chunk-PYBB34ZK.js} +54 -3
  13. package/dist/{chunk-R7UAGXQW.js → chunk-YCQSAFAQ.js} +42 -24
  14. package/dist/cli/dev.js +1 -1
  15. package/dist/cli/index.js +1 -1
  16. package/dist/{config-67AX6CNS.js → config-ACVEHBKG.js} +5 -5
  17. package/dist/{orchestrator-ZICQ5NIZ.js → orchestrator-OE7WFEW6.js} +5 -4
  18. package/dist/package.json +3 -3
  19. package/dist/{processor-MPSYT534.js → processor-GAOK7TPI.js} +2 -2
  20. package/dist/{protocol-I7rn7Msg.d.ts → protocol-BYM6rZvW.d.ts} +12 -0
  21. package/dist/{provider-DKGBQHUS.js → provider-RLQMVV2Z.js} +9 -8
  22. package/dist/{serve-B5A52252.js → serve-IBJ3SN3Q.js} +10 -10
  23. package/dist/server/index.d.ts +24 -1
  24. package/dist/server/index.js +8 -8
  25. package/dist/shared/index.d.ts +2 -2
  26. package/dist/shared/index.js +1 -1
  27. package/dist/{tools-ERJ3QRQU.js → tools-CSV7G554.js} +4 -3
  28. package/dist/{vision-fallback-ADYRFFD4.js → vision-fallback-QJ26DCEF.js} +2 -2
  29. package/dist/web/assets/index-BrZqZwpA.js +153 -0
  30. package/dist/web/assets/{index-vBklayFL.css → index-DF26vcqg.css} +1 -1
  31. package/dist/web/index.html +2 -2
  32. package/dist/web/sw.js +1 -1
  33. package/package.json +3 -3
  34. package/dist/chunk-55N6FAAZ.js +0 -117
  35. package/dist/web/assets/index-vOba1XKB.js +0 -151
@@ -1,7 +1,6 @@
1
1
  import {
2
- describeImageFromDataUrl,
3
- ensureVisionFallbackConfigLoaded
4
- } from "./chunk-IN5EP4ZB.js";
2
+ describeImageFromDataUrl
3
+ } from "./chunk-CJNQGEYG.js";
5
4
  import {
6
5
  logger
7
6
  } from "./chunk-PNBH3RAX.js";
@@ -167,134 +166,6 @@ function getBackendDisplayName(backend) {
167
166
  }
168
167
  }
169
168
 
170
- // src/server/llm/models.ts
171
- var modelCache = /* @__PURE__ */ new Map();
172
- var llmStatus = "unknown";
173
- var lastActiveUrl = null;
174
- var CACHE_TTL_MS = 3e4;
175
- function getCacheKey(url) {
176
- return url.replace(/\/v1\/?$/, "");
177
- }
178
- async function detectModel(llmBaseUrl, retries = 3, silent = false) {
179
- const cacheKey = getCacheKey(llmBaseUrl);
180
- const now = Date.now();
181
- const cached = modelCache.get(cacheKey);
182
- if (cached && now - cached.timestamp < CACHE_TTL_MS) {
183
- lastActiveUrl = cacheKey;
184
- llmStatus = "connected";
185
- return cached.model;
186
- }
187
- const url = llmBaseUrl.includes("/v1") ? `${llmBaseUrl}/models` : `${llmBaseUrl}/v1/models`;
188
- for (let attempt = 1; attempt <= retries; attempt++) {
189
- try {
190
- if (silent) {
191
- logger.debug("Fetching models from LLM server", { url, attempt });
192
- }
193
- const response = await fetch(url, {
194
- signal: AbortSignal.timeout(1e4)
195
- });
196
- if (!response.ok) {
197
- if (silent) {
198
- logger.debug("Failed to fetch models from LLM server", { status: response.status, attempt });
199
- } else {
200
- logger.warn("Failed to fetch models from LLM server", { status: response.status, attempt });
201
- }
202
- if (attempt < retries) {
203
- await new Promise((r) => setTimeout(r, 1e3 * attempt));
204
- continue;
205
- }
206
- llmStatus = "disconnected";
207
- return cached?.model ?? null;
208
- }
209
- const data = await response.json();
210
- if (data.data && data.data.length > 0) {
211
- const modelData = data.data[0];
212
- const modelId = modelData.id;
213
- modelCache.set(cacheKey, {
214
- model: modelId,
215
- modelInfo: modelData,
216
- timestamp: now
217
- });
218
- lastActiveUrl = cacheKey;
219
- llmStatus = "connected";
220
- if (silent) {
221
- logger.debug("Detected LLM model", {
222
- model: modelId,
223
- maxLen: modelData.max_model_len,
224
- root: modelData.root
225
- });
226
- } else {
227
- logger.info("Detected LLM model", {
228
- model: modelId,
229
- maxLen: modelData.max_model_len,
230
- root: modelData.root
231
- });
232
- }
233
- return modelId;
234
- }
235
- if (silent) {
236
- logger.debug("LLM server returned empty models list");
237
- } else {
238
- logger.warn("LLM server returned empty models list");
239
- }
240
- llmStatus = "disconnected";
241
- return null;
242
- } catch (error) {
243
- const errMsg = error instanceof Error ? error.message : String(error);
244
- if (silent) {
245
- logger.debug("Could not detect model from LLM server", { error: errMsg, attempt });
246
- } else {
247
- logger.warn("Could not detect model from LLM server", { error: errMsg, attempt });
248
- }
249
- if (attempt < retries) {
250
- await new Promise((r) => setTimeout(r, 1e3 * attempt));
251
- continue;
252
- }
253
- }
254
- }
255
- llmStatus = "disconnected";
256
- return cached?.model ?? null;
257
- }
258
- function getLlmStatus() {
259
- return llmStatus;
260
- }
261
- function setLlmStatus(status) {
262
- llmStatus = status;
263
- }
264
- function clearModelCache(url) {
265
- if (url) {
266
- modelCache.delete(getCacheKey(url));
267
- } else {
268
- modelCache.clear();
269
- }
270
- llmStatus = "unknown";
271
- }
272
-
273
- // src/server/llm/client.ts
274
- import OpenAI from "openai";
275
-
276
- // src/server/utils/errors.ts
277
- var OpenFoxError = class extends Error {
278
- constructor(message, code, details) {
279
- super(message);
280
- this.code = code;
281
- this.details = details;
282
- this.name = "OpenFoxError";
283
- }
284
- };
285
- var SessionNotFoundError = class extends OpenFoxError {
286
- constructor(sessionId) {
287
- super(`Session not found: ${sessionId}`, "SESSION_NOT_FOUND", { sessionId });
288
- this.name = "SessionNotFoundError";
289
- }
290
- };
291
- var LLMError = class extends OpenFoxError {
292
- constructor(message, details) {
293
- super(message, "LLM_ERROR", details);
294
- this.name = "LLMError";
295
- }
296
- };
297
-
298
169
  // src/server/llm/profiles.ts
299
170
  var DEFAULT_PROFILE = {
300
171
  name: "default",
@@ -493,6 +364,14 @@ function getModelProfile(modelName) {
493
364
  }
494
365
 
495
366
  // src/server/llm/client-pure.ts
367
+ function buildModelParams(params) {
368
+ return {
369
+ ...params.temperature !== void 0 && { temperature: params.temperature },
370
+ ...params.topP !== void 0 && { topP: params.topP },
371
+ ...params.topK !== void 0 && { topK: params.topK },
372
+ ...params.maxTokens !== void 0 && { maxTokens: params.maxTokens }
373
+ };
374
+ }
496
375
  function buildAttachmentContent(msgContent, attachments, modelSupportsVision) {
497
376
  const content = [];
498
377
  if (msgContent?.trim()) {
@@ -705,27 +584,32 @@ async function buildChatCompletionCreateParams(model, request, profile, capabili
705
584
  onVisionFallbackStart,
706
585
  onVisionFallbackDone
707
586
  );
587
+ const temperature = request.temperature ?? profile.temperature;
588
+ const maxTokens = request.maxTokens ?? profile.defaultMaxTokens;
589
+ const topP = profile.topP;
590
+ const topK = capabilities.supportsTopK ? profile.topK : void 0;
708
591
  const params = {
709
592
  model,
710
593
  messages: convertedMessages,
711
594
  ...request.tools ? { tools: convertTools(request.tools) } : {},
712
595
  ...request.toolChoice ? { tool_choice: request.toolChoice } : {},
713
- temperature: request.temperature ?? profile.temperature,
714
- max_tokens: request.maxTokens ?? profile.defaultMaxTokens,
715
- top_p: profile.topP,
596
+ temperature,
597
+ max_tokens: maxTokens,
598
+ top_p: topP,
716
599
  stream: isStreaming,
717
600
  ...isStreaming ? { stream_options: { include_usage: true } } : {}
718
601
  };
719
- if (capabilities.supportsTopK && profile.topK !== void 0) {
602
+ if (topK !== void 0) {
720
603
  ;
721
- params["top_k"] = profile.topK;
604
+ params["top_k"] = topK;
722
605
  }
723
606
  const shouldDisableThinking = isStreaming ? disableThinking || request.disableThinking : disableThinking;
724
607
  if (capabilities.supportsChatTemplateKwargs && profile.supportsReasoning && shouldDisableThinking) {
725
608
  ;
726
609
  params["chat_template_kwargs"] = { enable_thinking: false };
727
610
  }
728
- return params;
611
+ const modelParams = buildModelParams({ temperature, topP, topK, maxTokens });
612
+ return { params, modelParams };
729
613
  }
730
614
  async function buildCreateParamsFromInput(input, isStreaming) {
731
615
  const { model, request, profile, capabilities, disableThinking, visionFallbackEnabled = false, onVisionFallbackStart, onVisionFallbackDone } = input;
@@ -762,348 +646,129 @@ function extractThinking(content) {
762
646
  };
763
647
  }
764
648
 
765
- // src/server/llm/client.ts
766
- function createLLMClient(config, initialBackend = "unknown") {
767
- const baseURL = config.llm.baseUrl.includes("/v1") ? config.llm.baseUrl : `${config.llm.baseUrl}/v1`;
768
- const openai = new OpenAI({
769
- baseURL,
770
- apiKey: config.llm.apiKey ?? "not-needed"
771
- });
772
- let model = config.llm.model;
773
- let profile = getModelProfile(model);
774
- let backend = initialBackend;
775
- let capabilities = getBackendCapabilities(backend);
776
- const disableThinking = config.llm.disableThinking ?? false;
777
- const idleTimeout = config.llm.idleTimeout ?? 3e4;
778
- return {
779
- getModel() {
780
- return model;
781
- },
782
- getProfile() {
783
- return profile;
784
- },
785
- getBackend() {
786
- return backend;
787
- },
788
- setBackend(newBackend) {
789
- logger.debug("Setting LLM backend", { from: backend, to: newBackend });
790
- backend = newBackend;
791
- capabilities = getBackendCapabilities(newBackend);
792
- },
793
- setModel(newModel) {
794
- const newProfile = getModelProfile(newModel);
795
- logger.debug("Switching model", {
796
- from: model,
797
- to: newModel,
798
- profile: newProfile.name,
799
- temperature: newProfile.temperature,
800
- supportsReasoning: newProfile.supportsReasoning
801
- });
802
- model = newModel;
803
- profile = newProfile;
804
- },
805
- async complete(request) {
806
- logger.debug("LLM complete request", {
807
- messageCount: request.messages.length,
808
- hasTools: !!request.tools?.length,
809
- profile: profile.name,
810
- disableThinking
811
- });
812
- try {
813
- const shouldDisableThinking = disableThinking || request.disableThinking === true;
814
- await ensureVisionFallbackConfigLoaded();
815
- const { isVisionFallbackEnabled } = await import("./vision-fallback-ADYRFFD4.js");
816
- const paramsOptions = {
817
- model,
818
- request,
819
- profile,
820
- capabilities,
821
- disableThinking: shouldDisableThinking,
822
- visionFallbackEnabled: isVisionFallbackEnabled()
823
- };
824
- if (request.onVisionFallbackStart) {
825
- paramsOptions.onVisionFallbackStart = request.onVisionFallbackStart;
826
- }
827
- if (request.onVisionFallbackDone) {
828
- paramsOptions.onVisionFallbackDone = request.onVisionFallbackDone;
829
- }
830
- const createParams = await buildNonStreamingCreateParams(paramsOptions);
831
- const response = await openai.chat.completions.create(createParams, {
832
- signal: request.signal
833
- });
834
- const choice = response.choices[0];
835
- if (!choice) {
836
- throw new LLMError("No completion choice returned");
837
- }
838
- const message = choice.message;
839
- let content = message.content ?? "";
840
- let thinkingContent = "";
841
- if (profile.supportsReasoning) {
842
- if (capabilities.supportsReasoningField) {
843
- thinkingContent = message.reasoning_content ?? message.reasoning ?? "";
844
- } else {
845
- const extracted = extractThinking(content);
846
- content = extracted.content;
847
- thinkingContent = extracted.thinkingContent ?? "";
848
- }
849
- if (profile.reasoningAsContent && thinkingContent.trim()) {
850
- content = thinkingContent;
851
- thinkingContent = "";
852
- }
853
- }
854
- if (!content.trim() && thinkingContent.trim()) {
855
- content = thinkingContent;
856
- thinkingContent = "";
857
- }
858
- const toolCalls = message.tool_calls?.map((tc) => ({
859
- id: tc.id,
860
- name: tc.function.name,
861
- arguments: JSON.parse(tc.function.arguments)
862
- }));
863
- return {
864
- id: response.id,
865
- content,
866
- ...thinkingContent ? { thinkingContent } : {},
867
- ...toolCalls && toolCalls.length > 0 ? { toolCalls } : {},
868
- finishReason: mapFinishReason(choice.finish_reason),
869
- usage: {
870
- promptTokens: response.usage?.prompt_tokens ?? 0,
871
- completionTokens: response.usage?.completion_tokens ?? 0,
872
- totalTokens: response.usage?.total_tokens ?? 0
873
- }
874
- };
875
- } catch (error) {
876
- logger.error("LLM complete error", { error: error.toString() });
877
- throw new LLMError(
878
- error instanceof Error ? error.message : "Unknown LLM error",
879
- { originalError: error }
880
- );
881
- }
882
- },
883
- async *stream(request) {
884
- logger.debug("LLM stream request", {
885
- messageCount: request.messages.length,
886
- hasTools: !!request.tools?.length,
887
- profile: profile.name,
888
- disableThinking,
889
- idleTimeout
890
- });
891
- try {
892
- await ensureVisionFallbackConfigLoaded();
893
- const { isVisionFallbackEnabled } = await import("./vision-fallback-ADYRFFD4.js");
894
- const shouldDisableThinking = disableThinking || request.disableThinking === true;
895
- const createParams = await buildStreamingCreateParams({
896
- model,
897
- request,
898
- profile,
899
- capabilities,
900
- disableThinking: shouldDisableThinking,
901
- visionFallbackEnabled: isVisionFallbackEnabled(),
902
- onVisionFallbackStart: request.onVisionFallbackStart,
903
- onVisionFallbackDone: request.onVisionFallbackDone
904
- });
905
- const stream = await openai.chat.completions.create(createParams, {
906
- signal: request.signal
907
- });
908
- let fullContent = "";
909
- let fullThinking = "";
910
- let inThinking = false;
911
- let tagBuffer = "";
912
- const toolCalls = /* @__PURE__ */ new Map();
913
- let finishReason = "stop";
914
- let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
915
- let responseId = "";
916
- let lastChunkTime = Date.now();
917
- const idleTimeoutController = new AbortController();
918
- const idleTimer = setInterval(() => {
919
- const idleDuration = Date.now() - lastChunkTime;
920
- if (idleDuration > idleTimeout) {
921
- logger.warn("LLM stream idle timeout triggered", { idleDuration, idleTimeout });
922
- idleTimeoutController.abort();
923
- }
924
- }, 100);
925
- try {
926
- for await (const chunk of stream) {
927
- if (idleTimeoutController.signal.aborted) {
928
- throw new Error(`LLM stream idle timeout: no chunks received for ${idleTimeout}ms`);
929
- }
930
- lastChunkTime = Date.now();
931
- responseId = chunk.id;
932
- if (chunk.usage) {
933
- usage = {
934
- promptTokens: chunk.usage.prompt_tokens,
935
- completionTokens: chunk.usage.completion_tokens,
936
- totalTokens: chunk.usage.total_tokens
937
- };
938
- }
939
- const choice = chunk.choices[0];
940
- if (!choice) continue;
941
- if (choice.finish_reason) {
942
- finishReason = mapFinishReason(choice.finish_reason);
943
- }
944
- const delta = choice.delta;
945
- if (capabilities.supportsReasoningField) {
946
- const reasoning = delta.reasoning_content ?? delta.reasoning;
947
- if (reasoning) {
948
- if (profile.supportsReasoning && !profile.reasoningAsContent) {
949
- fullThinking += reasoning;
950
- yield { type: "thinking_delta", content: reasoning };
951
- } else {
952
- fullContent += reasoning;
953
- yield { type: "text_delta", content: reasoning };
954
- }
955
- }
956
- }
957
- if (delta.content) {
958
- fullContent += delta.content;
959
- if (!capabilities.supportsReasoningField && profile.supportsReasoning && !profile.reasoningAsContent) {
960
- tagBuffer += delta.content;
961
- while (tagBuffer.length > 0) {
962
- if (inThinking) {
963
- const closeIdx = tagBuffer.indexOf("</think>");
964
- if (closeIdx !== -1) {
965
- const thinkText = tagBuffer.slice(0, closeIdx);
966
- if (thinkText) {
967
- fullThinking += thinkText;
968
- yield { type: "thinking_delta", content: thinkText };
969
- }
970
- tagBuffer = tagBuffer.slice(closeIdx + "</think>".length);
971
- inThinking = false;
972
- } else if (tagBuffer.includes("</")) {
973
- const partialIdx = tagBuffer.lastIndexOf("</");
974
- const before = tagBuffer.slice(0, partialIdx);
975
- if (before) {
976
- fullThinking += before;
977
- yield { type: "thinking_delta", content: before };
978
- }
979
- tagBuffer = tagBuffer.slice(partialIdx);
980
- break;
981
- } else {
982
- fullThinking += tagBuffer;
983
- yield { type: "thinking_delta", content: tagBuffer };
984
- tagBuffer = "";
985
- }
986
- } else {
987
- const openIdx = tagBuffer.indexOf("<think>");
988
- if (openIdx !== -1) {
989
- const textBefore = tagBuffer.slice(0, openIdx);
990
- if (textBefore) {
991
- yield { type: "text_delta", content: textBefore };
992
- }
993
- tagBuffer = tagBuffer.slice(openIdx + "<think>".length);
994
- inThinking = true;
995
- } else if (tagBuffer.includes("<")) {
996
- const partialIdx = tagBuffer.lastIndexOf("<");
997
- const before = tagBuffer.slice(0, partialIdx);
998
- if (before) {
999
- yield { type: "text_delta", content: before };
1000
- }
1001
- tagBuffer = tagBuffer.slice(partialIdx);
1002
- break;
1003
- } else {
1004
- yield { type: "text_delta", content: tagBuffer };
1005
- tagBuffer = "";
1006
- }
1007
- }
1008
- }
1009
- } else {
1010
- yield { type: "text_delta", content: delta.content };
1011
- }
1012
- }
1013
- if (delta.tool_calls) {
1014
- for (const tc of delta.tool_calls) {
1015
- const existing = toolCalls.get(tc.index);
1016
- if (!existing) {
1017
- toolCalls.set(tc.index, {
1018
- id: tc.id ?? "",
1019
- name: tc.function?.name ?? "",
1020
- arguments: tc.function?.arguments ?? ""
1021
- });
1022
- } else {
1023
- if (tc.id) existing.id = tc.id;
1024
- if (tc.function?.name) existing.name += tc.function.name;
1025
- if (tc.function?.arguments) existing.arguments += tc.function.arguments;
1026
- }
1027
- yield {
1028
- type: "tool_call_delta",
1029
- index: tc.index,
1030
- ...tc.id ? { id: tc.id } : {},
1031
- ...tc.function?.name ? { name: tc.function.name } : {},
1032
- ...tc.function?.arguments ? { arguments: tc.function.arguments } : {}
1033
- };
1034
- }
1035
- }
649
+ // src/server/llm/streaming.ts
650
+ var XML_TOOL_PATTERNS = ["<tool_call>", "<function=", "</tool_call>", "<parameter="];
651
+ function hasXmlToolPattern(text) {
652
+ return XML_TOOL_PATTERNS.some((p) => text.includes(p));
653
+ }
654
+ async function* streamWithSegments(client, request) {
655
+ const xmlAbortController = new AbortController();
656
+ const combinedSignal = request.signal ? AbortSignal.any([request.signal, xmlAbortController.signal]) : xmlAbortController.signal;
657
+ let content = "";
658
+ let thinkingContent = "";
659
+ let response = null;
660
+ const segments = [];
661
+ let currentTextSegment = "";
662
+ let currentThinkingSegment = "";
663
+ const startTime = performance.now();
664
+ let firstTokenTime = null;
665
+ const flushText = () => {
666
+ if (currentTextSegment.trim()) {
667
+ segments.push({ type: "text", content: currentTextSegment });
668
+ }
669
+ currentTextSegment = "";
670
+ };
671
+ const flushThinking = () => {
672
+ if (currentThinkingSegment.trim()) {
673
+ segments.push({ type: "thinking", content: currentThinkingSegment });
674
+ }
675
+ currentThinkingSegment = "";
676
+ };
677
+ try {
678
+ for await (const event of client.stream({ ...request, signal: combinedSignal })) {
679
+ switch (event.type) {
680
+ case "text_delta":
681
+ if (firstTokenTime === null) {
682
+ firstTokenTime = performance.now();
1036
683
  }
1037
- } finally {
1038
- clearInterval(idleTimer);
1039
- }
1040
- if (tagBuffer) {
1041
- if (inThinking) {
1042
- fullThinking += tagBuffer;
684
+ flushThinking();
685
+ content += event.content;
686
+ currentTextSegment += event.content;
687
+ if (hasXmlToolPattern(content)) {
688
+ xmlAbortController.abort();
689
+ yield { type: "xml_tool_abort" };
690
+ return null;
1043
691
  }
1044
- tagBuffer = "";
1045
- }
1046
- let finalContent = fullContent.trim();
1047
- let finalThinking = fullThinking.trim();
1048
- if (!capabilities.supportsReasoningField && profile.supportsReasoning) {
1049
- const extracted = extractThinking(finalContent);
1050
- finalContent = extracted.content;
1051
- finalThinking = extracted.thinkingContent ?? "";
1052
- }
1053
- if (!finalContent && finalThinking) {
1054
- finalContent = finalThinking;
1055
- finalThinking = "";
1056
- }
1057
- const parsedToolCalls = [];
1058
- for (const [, tc] of toolCalls) {
1059
- try {
1060
- parsedToolCalls.push({
1061
- id: tc.id,
1062
- name: tc.name,
1063
- arguments: JSON.parse(tc.arguments)
1064
- });
1065
- } catch (error) {
1066
- logger.warn("Failed to parse tool call arguments", { name: tc.name, arguments: tc.arguments });
1067
- parsedToolCalls.push({
1068
- id: tc.id,
1069
- name: tc.name,
1070
- arguments: {},
1071
- parseError: error instanceof Error ? error.message : "Unknown JSON parse error",
1072
- rawArguments: tc.arguments
1073
- });
692
+ yield { type: "text_delta", content: event.content };
693
+ break;
694
+ case "thinking_delta":
695
+ if (firstTokenTime === null) {
696
+ firstTokenTime = performance.now();
1074
697
  }
1075
- }
1076
- yield {
1077
- type: "done",
1078
- response: {
1079
- id: responseId,
1080
- content: finalContent,
1081
- ...finalThinking ? { thinkingContent: finalThinking } : {},
1082
- ...parsedToolCalls.length > 0 ? { toolCalls: parsedToolCalls } : {},
1083
- finishReason,
1084
- usage
698
+ flushText();
699
+ thinkingContent += event.content;
700
+ currentThinkingSegment += event.content;
701
+ if (hasXmlToolPattern(thinkingContent)) {
702
+ xmlAbortController.abort();
703
+ yield { type: "xml_tool_abort" };
704
+ return null;
1085
705
  }
1086
- };
1087
- } catch (error) {
1088
- logger.error("LLM stream error", { error });
1089
- yield {
1090
- type: "error",
1091
- error: error instanceof Error ? error.message : "Unknown LLM error"
1092
- };
706
+ yield { type: "thinking_delta", content: event.content };
707
+ break;
708
+ case "tool_call_delta":
709
+ yield {
710
+ type: "tool_call_delta",
711
+ index: event.index,
712
+ ...event.id !== void 0 ? { id: event.id } : {},
713
+ ...event.name !== void 0 ? { name: event.name } : {},
714
+ ...event.arguments !== void 0 ? { arguments: event.arguments } : {}
715
+ };
716
+ break;
717
+ case "done":
718
+ flushThinking();
719
+ flushText();
720
+ response = event.response;
721
+ yield { type: "done", response: event.response };
722
+ break;
723
+ case "error":
724
+ yield { type: "error", error: event.error };
725
+ return null;
1093
726
  }
1094
727
  }
728
+ } catch (error) {
729
+ if (error instanceof Error && error.name === "AbortError") {
730
+ return null;
731
+ }
732
+ yield { type: "error", error: error instanceof Error ? error.message : "Unknown error" };
733
+ return null;
734
+ }
735
+ if (!response) {
736
+ return null;
737
+ }
738
+ const toolCalls = response.toolCalls ?? [];
739
+ for (const tc of toolCalls) {
740
+ segments.push({ type: "tool_call", toolCallId: tc.id });
741
+ }
742
+ const endTime = performance.now();
743
+ const ttft = ((firstTokenTime ?? endTime) - startTime) / 1e3;
744
+ const completionTime = (endTime - (firstTokenTime ?? startTime)) / 1e3;
745
+ const { promptTokens, completionTokens } = response.usage;
746
+ const effectiveThinkingContent = thinkingContent || content === "" && response.reasoning_content || "";
747
+ return {
748
+ content,
749
+ thinkingContent: effectiveThinkingContent,
750
+ toolCalls,
751
+ response,
752
+ segments,
753
+ timing: {
754
+ ttft,
755
+ completionTime,
756
+ tps: completionTime > 0 ? completionTokens / completionTime : 0,
757
+ prefillTps: ttft > 0 ? promptTokens / ttft : 0
758
+ }
1095
759
  };
1096
760
  }
1097
761
 
1098
762
  export {
1099
- SessionNotFoundError,
1100
763
  getModelProfile,
764
+ getBackendCapabilities,
1101
765
  detectBackend,
1102
766
  getBackendDisplayName,
1103
- createLLMClient,
1104
- detectModel,
1105
- getLlmStatus,
1106
- setLlmStatus,
1107
- clearModelCache
767
+ buildModelParams,
768
+ buildNonStreamingCreateParams,
769
+ buildStreamingCreateParams,
770
+ mapFinishReason,
771
+ extractThinking,
772
+ streamWithSegments
1108
773
  };
1109
- //# sourceMappingURL=chunk-OVLFEBRR.js.map
774
+ //# sourceMappingURL=chunk-D4ZLSV6P.js.map