@browser-ai/web-llm 2.1.4 → 2.1.6
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.js +206 -197
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +206 -197
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -858,7 +858,182 @@ function extractArgumentsDelta(content, state) {
|
|
|
858
858
|
return delta;
|
|
859
859
|
}
|
|
860
860
|
|
|
861
|
-
// src/
|
|
861
|
+
// ../shared/src/streaming/stream-processor.ts
|
|
862
|
+
function generateToolCallId2() {
|
|
863
|
+
return `call_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
864
|
+
}
|
|
865
|
+
async function processToolCallStream(chunks, emitTextDelta, controller, options) {
|
|
866
|
+
const fenceDetector = new ToolCallFenceDetector();
|
|
867
|
+
let currentToolCallId = null;
|
|
868
|
+
let toolInputStartEmitted = false;
|
|
869
|
+
let accumulatedFenceContent = "";
|
|
870
|
+
let argumentsStreamState = createArgumentsStreamState();
|
|
871
|
+
let insideFence = false;
|
|
872
|
+
let toolCallDetected = false;
|
|
873
|
+
let toolCalls = [];
|
|
874
|
+
let trailingText = "";
|
|
875
|
+
const resetFenceState = () => {
|
|
876
|
+
currentToolCallId = null;
|
|
877
|
+
toolInputStartEmitted = false;
|
|
878
|
+
accumulatedFenceContent = "";
|
|
879
|
+
argumentsStreamState = createArgumentsStreamState();
|
|
880
|
+
insideFence = false;
|
|
881
|
+
};
|
|
882
|
+
for await (const chunk of chunks) {
|
|
883
|
+
if (toolCallDetected) {
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
fenceDetector.addChunk(chunk);
|
|
887
|
+
while (fenceDetector.hasContent()) {
|
|
888
|
+
const wasInsideFence = insideFence;
|
|
889
|
+
const result = fenceDetector.detectStreamingFence();
|
|
890
|
+
insideFence = result.inFence;
|
|
891
|
+
let madeProgress = false;
|
|
892
|
+
if (!wasInsideFence && result.inFence) {
|
|
893
|
+
if (result.safeContent) {
|
|
894
|
+
emitTextDelta(result.safeContent);
|
|
895
|
+
madeProgress = true;
|
|
896
|
+
}
|
|
897
|
+
currentToolCallId = generateToolCallId2();
|
|
898
|
+
toolInputStartEmitted = false;
|
|
899
|
+
accumulatedFenceContent = "";
|
|
900
|
+
argumentsStreamState = createArgumentsStreamState();
|
|
901
|
+
insideFence = true;
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (result.completeFence) {
|
|
905
|
+
madeProgress = true;
|
|
906
|
+
if (result.safeContent) {
|
|
907
|
+
accumulatedFenceContent += result.safeContent;
|
|
908
|
+
}
|
|
909
|
+
if (toolInputStartEmitted && currentToolCallId) {
|
|
910
|
+
const delta = extractArgumentsDelta(
|
|
911
|
+
accumulatedFenceContent,
|
|
912
|
+
argumentsStreamState
|
|
913
|
+
);
|
|
914
|
+
if (delta.length > 0) {
|
|
915
|
+
controller.enqueue({
|
|
916
|
+
type: "tool-input-delta",
|
|
917
|
+
id: currentToolCallId,
|
|
918
|
+
delta
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const parsed = parseJsonFunctionCalls(result.completeFence);
|
|
923
|
+
const selectedToolCalls = parsed.toolCalls.slice(0, 1);
|
|
924
|
+
if (selectedToolCalls.length === 0) {
|
|
925
|
+
emitTextDelta(result.completeFence);
|
|
926
|
+
if (result.textAfterFence) {
|
|
927
|
+
emitTextDelta(result.textAfterFence);
|
|
928
|
+
}
|
|
929
|
+
resetFenceState();
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
if (currentToolCallId) {
|
|
933
|
+
selectedToolCalls[0].toolCallId = currentToolCallId;
|
|
934
|
+
}
|
|
935
|
+
for (const [index, call] of selectedToolCalls.entries()) {
|
|
936
|
+
const toolCallId = index === 0 && currentToolCallId ? currentToolCallId : call.toolCallId;
|
|
937
|
+
const toolName = call.toolName;
|
|
938
|
+
const argsJson = JSON.stringify(call.args ?? {});
|
|
939
|
+
if (toolCallId === currentToolCallId) {
|
|
940
|
+
if (!toolInputStartEmitted) {
|
|
941
|
+
controller.enqueue({
|
|
942
|
+
type: "tool-input-start",
|
|
943
|
+
id: toolCallId,
|
|
944
|
+
toolName
|
|
945
|
+
});
|
|
946
|
+
toolInputStartEmitted = true;
|
|
947
|
+
}
|
|
948
|
+
const delta = extractArgumentsDelta(
|
|
949
|
+
accumulatedFenceContent,
|
|
950
|
+
argumentsStreamState
|
|
951
|
+
);
|
|
952
|
+
if (delta.length > 0) {
|
|
953
|
+
controller.enqueue({
|
|
954
|
+
type: "tool-input-delta",
|
|
955
|
+
id: toolCallId,
|
|
956
|
+
delta
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
} else {
|
|
960
|
+
controller.enqueue({
|
|
961
|
+
type: "tool-input-start",
|
|
962
|
+
id: toolCallId,
|
|
963
|
+
toolName
|
|
964
|
+
});
|
|
965
|
+
if (argsJson.length > 0) {
|
|
966
|
+
controller.enqueue({
|
|
967
|
+
type: "tool-input-delta",
|
|
968
|
+
id: toolCallId,
|
|
969
|
+
delta: argsJson
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
controller.enqueue({ type: "tool-input-end", id: toolCallId });
|
|
974
|
+
controller.enqueue({
|
|
975
|
+
type: "tool-call",
|
|
976
|
+
toolCallId,
|
|
977
|
+
toolName,
|
|
978
|
+
input: argsJson,
|
|
979
|
+
providerExecuted: false
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
trailingText = result.textAfterFence ?? "";
|
|
983
|
+
toolCalls = selectedToolCalls;
|
|
984
|
+
toolCallDetected = true;
|
|
985
|
+
resetFenceState();
|
|
986
|
+
break;
|
|
987
|
+
}
|
|
988
|
+
if (insideFence) {
|
|
989
|
+
if (result.safeContent) {
|
|
990
|
+
accumulatedFenceContent += result.safeContent;
|
|
991
|
+
madeProgress = true;
|
|
992
|
+
const toolName = extractToolName(accumulatedFenceContent);
|
|
993
|
+
if (toolName && !toolInputStartEmitted && currentToolCallId) {
|
|
994
|
+
controller.enqueue({
|
|
995
|
+
type: "tool-input-start",
|
|
996
|
+
id: currentToolCallId,
|
|
997
|
+
toolName
|
|
998
|
+
});
|
|
999
|
+
toolInputStartEmitted = true;
|
|
1000
|
+
}
|
|
1001
|
+
if (toolInputStartEmitted && currentToolCallId) {
|
|
1002
|
+
const delta = extractArgumentsDelta(
|
|
1003
|
+
accumulatedFenceContent,
|
|
1004
|
+
argumentsStreamState
|
|
1005
|
+
);
|
|
1006
|
+
if (delta.length > 0) {
|
|
1007
|
+
controller.enqueue({
|
|
1008
|
+
type: "tool-input-delta",
|
|
1009
|
+
id: currentToolCallId,
|
|
1010
|
+
delta
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (!insideFence && result.safeContent) {
|
|
1018
|
+
emitTextDelta(result.safeContent);
|
|
1019
|
+
madeProgress = true;
|
|
1020
|
+
}
|
|
1021
|
+
if (!madeProgress) {
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (toolCallDetected && options?.stopEarlyOnToolCall) {
|
|
1026
|
+
break;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (!toolCallDetected && fenceDetector.hasContent()) {
|
|
1030
|
+
emitTextDelta(fenceDetector.getBuffer());
|
|
1031
|
+
fenceDetector.clearBuffer();
|
|
1032
|
+
}
|
|
1033
|
+
return { toolCallDetected, toolCalls, trailingText };
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/utils/convert-to-webllm-messages.tsx
|
|
862
1037
|
function convertToolResultOutput(output) {
|
|
863
1038
|
switch (output.type) {
|
|
864
1039
|
case "text":
|
|
@@ -997,7 +1172,7 @@ function convertToWebLLMMessages(prompt) {
|
|
|
997
1172
|
return messages;
|
|
998
1173
|
}
|
|
999
1174
|
|
|
1000
|
-
// src/web-llm-language-model.ts
|
|
1175
|
+
// src/chat/web-llm-language-model.ts
|
|
1001
1176
|
import {
|
|
1002
1177
|
CreateWebWorkerMLCEngine,
|
|
1003
1178
|
MLCEngine
|
|
@@ -1060,7 +1235,7 @@ function doesBrowserSupportWebLLM() {
|
|
|
1060
1235
|
return checkWebGPU();
|
|
1061
1236
|
}
|
|
1062
1237
|
|
|
1063
|
-
// src/web-llm-language-model.ts
|
|
1238
|
+
// src/chat/web-llm-language-model.ts
|
|
1064
1239
|
var WebLLMLanguageModel = class {
|
|
1065
1240
|
constructor(modelId, options = {}) {
|
|
1066
1241
|
this.specificationVersion = "v3";
|
|
@@ -1214,10 +1389,12 @@ var WebLLMLanguageModel = class {
|
|
|
1214
1389
|
top_p: topP,
|
|
1215
1390
|
seed
|
|
1216
1391
|
};
|
|
1217
|
-
|
|
1392
|
+
const webLLMOptions = providerOptions?.[this.provider];
|
|
1393
|
+
const extraBody = webLLMOptions?.extra_body;
|
|
1394
|
+
if (extraBody) {
|
|
1218
1395
|
requestOptions.extra_body = {
|
|
1219
|
-
enable_thinking:
|
|
1220
|
-
enable_latency_breakdown:
|
|
1396
|
+
enable_thinking: extraBody.enable_thinking,
|
|
1397
|
+
enable_latency_breakdown: extraBody.enable_latency_breakdown
|
|
1221
1398
|
};
|
|
1222
1399
|
}
|
|
1223
1400
|
if (responseFormat?.type === "json") {
|
|
@@ -1313,7 +1490,7 @@ var WebLLMLanguageModel = class {
|
|
|
1313
1490
|
reasoning: void 0
|
|
1314
1491
|
}
|
|
1315
1492
|
},
|
|
1316
|
-
request: { body: { messages: promptMessages
|
|
1493
|
+
request: { body: { ...requestOptions, messages: promptMessages } },
|
|
1317
1494
|
warnings
|
|
1318
1495
|
};
|
|
1319
1496
|
}
|
|
@@ -1349,7 +1526,7 @@ var WebLLMLanguageModel = class {
|
|
|
1349
1526
|
total: response.usage?.total_tokens
|
|
1350
1527
|
}
|
|
1351
1528
|
},
|
|
1352
|
-
request: { body: { messages: promptMessages
|
|
1529
|
+
request: { body: { ...requestOptions, messages: promptMessages } },
|
|
1353
1530
|
warnings
|
|
1354
1531
|
};
|
|
1355
1532
|
} catch (error) {
|
|
@@ -1504,195 +1681,27 @@ var WebLLMLanguageModel = class {
|
|
|
1504
1681
|
...options.abortSignal && !useWorker && { signal: options.abortSignal }
|
|
1505
1682
|
};
|
|
1506
1683
|
const response = await engine.chat.completions.create(streamingRequest);
|
|
1507
|
-
|
|
1508
|
-
let
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
if (!choice) continue;
|
|
1517
|
-
if (choice.delta.content) {
|
|
1518
|
-
const delta = choice.delta.content;
|
|
1519
|
-
accumulatedText += delta;
|
|
1520
|
-
fenceDetector.addChunk(delta);
|
|
1521
|
-
while (fenceDetector.hasContent()) {
|
|
1522
|
-
const wasInsideFence = insideFence;
|
|
1523
|
-
const result = fenceDetector.detectStreamingFence();
|
|
1524
|
-
insideFence = result.inFence;
|
|
1525
|
-
let madeProgress = false;
|
|
1526
|
-
if (!wasInsideFence && result.inFence) {
|
|
1527
|
-
if (result.safeContent) {
|
|
1528
|
-
emitTextDelta(result.safeContent);
|
|
1529
|
-
madeProgress = true;
|
|
1530
|
-
}
|
|
1531
|
-
currentToolCallId = `call_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
1532
|
-
toolInputStartEmitted = false;
|
|
1533
|
-
accumulatedFenceContent = "";
|
|
1534
|
-
argumentsStreamState = createArgumentsStreamState();
|
|
1535
|
-
insideFence = true;
|
|
1536
|
-
continue;
|
|
1537
|
-
}
|
|
1538
|
-
if (result.completeFence) {
|
|
1539
|
-
madeProgress = true;
|
|
1540
|
-
if (result.safeContent) {
|
|
1541
|
-
accumulatedFenceContent += result.safeContent;
|
|
1542
|
-
}
|
|
1543
|
-
if (toolInputStartEmitted && currentToolCallId) {
|
|
1544
|
-
const delta2 = extractArgumentsDelta(
|
|
1545
|
-
accumulatedFenceContent,
|
|
1546
|
-
argumentsStreamState
|
|
1547
|
-
);
|
|
1548
|
-
if (delta2.length > 0) {
|
|
1549
|
-
controller.enqueue({
|
|
1550
|
-
type: "tool-input-delta",
|
|
1551
|
-
id: currentToolCallId,
|
|
1552
|
-
delta: delta2
|
|
1553
|
-
});
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
const parsed = parseJsonFunctionCalls(result.completeFence);
|
|
1557
|
-
const parsedToolCalls = parsed.toolCalls;
|
|
1558
|
-
const selectedToolCalls = parsedToolCalls.slice(0, 1);
|
|
1559
|
-
if (selectedToolCalls.length === 0) {
|
|
1560
|
-
emitTextDelta(result.completeFence);
|
|
1561
|
-
if (result.textAfterFence) {
|
|
1562
|
-
emitTextDelta(result.textAfterFence);
|
|
1563
|
-
}
|
|
1564
|
-
currentToolCallId = null;
|
|
1565
|
-
toolInputStartEmitted = false;
|
|
1566
|
-
accumulatedFenceContent = "";
|
|
1567
|
-
argumentsStreamState = createArgumentsStreamState();
|
|
1568
|
-
insideFence = false;
|
|
1569
|
-
continue;
|
|
1570
|
-
}
|
|
1571
|
-
if (selectedToolCalls.length > 0 && currentToolCallId) {
|
|
1572
|
-
selectedToolCalls[0].toolCallId = currentToolCallId;
|
|
1573
|
-
}
|
|
1574
|
-
for (const [index, call] of selectedToolCalls.entries()) {
|
|
1575
|
-
const toolCallId = index === 0 && currentToolCallId ? currentToolCallId : call.toolCallId;
|
|
1576
|
-
const toolName = call.toolName;
|
|
1577
|
-
const argsJson = JSON.stringify(call.args ?? {});
|
|
1578
|
-
if (toolCallId === currentToolCallId) {
|
|
1579
|
-
if (!toolInputStartEmitted) {
|
|
1580
|
-
controller.enqueue({
|
|
1581
|
-
type: "tool-input-start",
|
|
1582
|
-
id: toolCallId,
|
|
1583
|
-
toolName
|
|
1584
|
-
});
|
|
1585
|
-
toolInputStartEmitted = true;
|
|
1586
|
-
}
|
|
1587
|
-
const delta2 = extractArgumentsDelta(
|
|
1588
|
-
accumulatedFenceContent,
|
|
1589
|
-
argumentsStreamState
|
|
1590
|
-
);
|
|
1591
|
-
if (delta2.length > 0) {
|
|
1592
|
-
controller.enqueue({
|
|
1593
|
-
type: "tool-input-delta",
|
|
1594
|
-
id: toolCallId,
|
|
1595
|
-
delta: delta2
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
} else {
|
|
1599
|
-
controller.enqueue({
|
|
1600
|
-
type: "tool-input-start",
|
|
1601
|
-
id: toolCallId,
|
|
1602
|
-
toolName
|
|
1603
|
-
});
|
|
1604
|
-
if (argsJson.length > 0) {
|
|
1605
|
-
controller.enqueue({
|
|
1606
|
-
type: "tool-input-delta",
|
|
1607
|
-
id: toolCallId,
|
|
1608
|
-
delta: argsJson
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
controller.enqueue({
|
|
1613
|
-
type: "tool-input-end",
|
|
1614
|
-
id: toolCallId
|
|
1615
|
-
});
|
|
1616
|
-
controller.enqueue({
|
|
1617
|
-
type: "tool-call",
|
|
1618
|
-
toolCallId,
|
|
1619
|
-
toolName,
|
|
1620
|
-
input: argsJson,
|
|
1621
|
-
providerExecuted: false
|
|
1622
|
-
});
|
|
1623
|
-
}
|
|
1624
|
-
if (result.textAfterFence) {
|
|
1625
|
-
emitTextDelta(result.textAfterFence);
|
|
1626
|
-
}
|
|
1627
|
-
madeProgress = true;
|
|
1628
|
-
currentToolCallId = null;
|
|
1629
|
-
toolInputStartEmitted = false;
|
|
1630
|
-
accumulatedFenceContent = "";
|
|
1631
|
-
argumentsStreamState = createArgumentsStreamState();
|
|
1632
|
-
insideFence = false;
|
|
1633
|
-
continue;
|
|
1634
|
-
}
|
|
1635
|
-
if (insideFence) {
|
|
1636
|
-
if (result.safeContent) {
|
|
1637
|
-
accumulatedFenceContent += result.safeContent;
|
|
1638
|
-
madeProgress = true;
|
|
1639
|
-
const toolName = extractToolName(accumulatedFenceContent);
|
|
1640
|
-
if (toolName && !toolInputStartEmitted && currentToolCallId) {
|
|
1641
|
-
controller.enqueue({
|
|
1642
|
-
type: "tool-input-start",
|
|
1643
|
-
id: currentToolCallId,
|
|
1644
|
-
toolName
|
|
1645
|
-
});
|
|
1646
|
-
toolInputStartEmitted = true;
|
|
1647
|
-
}
|
|
1648
|
-
if (toolInputStartEmitted && currentToolCallId) {
|
|
1649
|
-
const delta2 = extractArgumentsDelta(
|
|
1650
|
-
accumulatedFenceContent,
|
|
1651
|
-
argumentsStreamState
|
|
1652
|
-
);
|
|
1653
|
-
if (delta2.length > 0) {
|
|
1654
|
-
controller.enqueue({
|
|
1655
|
-
type: "tool-input-delta",
|
|
1656
|
-
id: currentToolCallId,
|
|
1657
|
-
delta: delta2
|
|
1658
|
-
});
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
continue;
|
|
1663
|
-
}
|
|
1664
|
-
if (!insideFence && result.safeContent) {
|
|
1665
|
-
emitTextDelta(result.safeContent);
|
|
1666
|
-
madeProgress = true;
|
|
1667
|
-
}
|
|
1668
|
-
if (!madeProgress) {
|
|
1669
|
-
break;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1684
|
+
let lastUsage;
|
|
1685
|
+
let isAbort = false;
|
|
1686
|
+
const chunks = (async function* () {
|
|
1687
|
+
for await (const chunk of response) {
|
|
1688
|
+
const choice = chunk.choices[0];
|
|
1689
|
+
if (!choice) continue;
|
|
1690
|
+
if (choice.delta.content) yield choice.delta.content;
|
|
1691
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
1692
|
+
if (choice.finish_reason === "abort") isAbort = true;
|
|
1672
1693
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
};
|
|
1682
|
-
if (choice.finish_reason === "abort") {
|
|
1683
|
-
finishReason = { unified: "other", raw: "abort" };
|
|
1684
|
-
} else {
|
|
1685
|
-
const { toolCalls } = parseJsonFunctionCalls(accumulatedText);
|
|
1686
|
-
if (toolCalls.length > 0) {
|
|
1687
|
-
finishReason = { unified: "tool-calls", raw: "tool-calls" };
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
finishStream(finishReason, chunk.usage);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
if (!finished) {
|
|
1694
|
-
finishStream({ unified: "stop", raw: "stop" });
|
|
1694
|
+
})();
|
|
1695
|
+
const result = await processToolCallStream(
|
|
1696
|
+
chunks,
|
|
1697
|
+
emitTextDelta,
|
|
1698
|
+
controller
|
|
1699
|
+
);
|
|
1700
|
+
if (result.trailingText) {
|
|
1701
|
+
emitTextDelta(result.trailingText);
|
|
1695
1702
|
}
|
|
1703
|
+
const finishReason = isAbort ? { unified: "other", raw: "abort" } : result.toolCallDetected ? { unified: "tool-calls", raw: "tool-calls" } : { unified: "stop", raw: "stop" };
|
|
1704
|
+
finishStream(finishReason, lastUsage);
|
|
1696
1705
|
} catch (error) {
|
|
1697
1706
|
controller.error(error);
|
|
1698
1707
|
} finally {
|
|
@@ -1707,12 +1716,12 @@ var WebLLMLanguageModel = class {
|
|
|
1707
1716
|
});
|
|
1708
1717
|
return {
|
|
1709
1718
|
stream,
|
|
1710
|
-
request: { body: { messages: promptMessages
|
|
1719
|
+
request: { body: { ...requestOptions, messages: promptMessages } }
|
|
1711
1720
|
};
|
|
1712
1721
|
}
|
|
1713
1722
|
};
|
|
1714
1723
|
|
|
1715
|
-
// src/web-llm-embedding-model.ts
|
|
1724
|
+
// src/embedding/web-llm-embedding-model.ts
|
|
1716
1725
|
import {
|
|
1717
1726
|
CreateWebWorkerMLCEngine as CreateWebWorkerMLCEngine2,
|
|
1718
1727
|
MLCEngine as MLCEngine2
|