utilitas 1998.2.35 → 1998.2.36
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/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/alan.mjs +91 -106
- package/lib/manifest.mjs +1 -1
- package/package.json +1 -1
package/lib/alan.mjs
CHANGED
|
@@ -743,14 +743,18 @@ const packResp = async (resp, options) => {
|
|
|
743
743
|
};
|
|
744
744
|
};
|
|
745
745
|
|
|
746
|
+
const streamResp = async (resp, options) => {
|
|
747
|
+
const msg = await packGptResp(resp, { ...options, processing: true });
|
|
748
|
+
return options?.stream && (msg?.text || msg?.audio?.length)
|
|
749
|
+
&& await ignoreErrFunc(async () => await options.stream(msg), LOG);
|
|
750
|
+
};
|
|
751
|
+
|
|
746
752
|
const packGptResp = async (resp, options) => {
|
|
747
753
|
// simple mode is not recommended for streaming responses
|
|
748
|
-
let text = resp
|
|
749
|
-
|| resp?.choices?.[0]?.message?.audio?.transcript // ChatGPT audio mode
|
|
754
|
+
let text = resp.text // ChatGPT / Claude
|
|
750
755
|
|| (Function.isFunction(resp?.text) ? resp.text() : resp?.text) // Gemini
|
|
751
|
-
|| resp?.content?.find(x => x.type === TEXT)?.text // Claude
|
|
752
756
|
|| resp?.message?.content || ''; // Ollama
|
|
753
|
-
const audio = resp?.
|
|
757
|
+
const audio = resp?.message?.audio?.data; // ChatGPT audio mode
|
|
754
758
|
if (options?.raw) { return resp; }
|
|
755
759
|
else if (options?.simple && options?.jsonMode) { return parseJson(text); }
|
|
756
760
|
else if (options?.simple && options?.audioMode) { return audio; }
|
|
@@ -769,24 +773,24 @@ const packGptResp = async (resp, options) => {
|
|
|
769
773
|
};
|
|
770
774
|
|
|
771
775
|
const handleToolsCall = async (msg, options) => {
|
|
772
|
-
let [content, preRes, input, packMsg, toolsResponse]
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
776
|
+
let [content, preRes, input, packMsg, toolsResponse, responded] = [
|
|
777
|
+
[], [], [], null, options?.result ? options?.result.trim() : '', false
|
|
778
|
+
];
|
|
779
|
+
const resp = async m => {
|
|
780
|
+
m = `\n${m}`;
|
|
781
|
+
responded || (m = `\n\n${TOOLS_STR}${m}`);
|
|
782
|
+
responded = true;
|
|
783
|
+
toolsResponse = (toolsResponse + m).trim();
|
|
784
|
+
await streamResp({ text: options?.delta ? m : toolsResponse }, options);
|
|
781
785
|
};
|
|
782
|
-
|
|
783
|
-
|
|
786
|
+
const calls = msg.tool_calls || msg.content || [];
|
|
787
|
+
if (calls.length) {
|
|
784
788
|
switch (options?.flavor) {
|
|
785
|
-
case CLAUDE: preRes.push(
|
|
789
|
+
case CLAUDE: preRes.push(msg); break;
|
|
786
790
|
case GEMINI: preRes.push({ role: MODEL, parts: msg?.tool_calls.map(x => ({ functionCall: x })) }); break;
|
|
787
|
-
case CHATGPT: default: preRes.push(
|
|
791
|
+
case CHATGPT: default: preRes.push(msg); break;
|
|
788
792
|
}
|
|
789
|
-
for (const fn of
|
|
793
|
+
for (const fn of calls) {
|
|
790
794
|
switch (options?.flavor) {
|
|
791
795
|
case CLAUDE:
|
|
792
796
|
input = fn.input = String.isString(fn?.input) ? parseJson(fn.input) : fn?.input;
|
|
@@ -813,12 +817,15 @@ const handleToolsCall = async (msg, options) => {
|
|
|
813
817
|
break;
|
|
814
818
|
}
|
|
815
819
|
const name = fn?.function?.name || fn?.name;
|
|
820
|
+
if (!name) { continue; }
|
|
816
821
|
await resp(`\nName: ${name}`);
|
|
817
822
|
const f = tools.find(x => insensitiveCompare(
|
|
818
823
|
x.def?.function?.name || x?.def?.name, name
|
|
819
824
|
));
|
|
820
825
|
if (!f?.func) {
|
|
821
|
-
|
|
826
|
+
const rt = `Failed: invalid function name \`${name}\``;
|
|
827
|
+
content.push(packMsg(rt, true));
|
|
828
|
+
await resp(rt);
|
|
822
829
|
continue;
|
|
823
830
|
}
|
|
824
831
|
const description = f.def?.function?.description || f.def?.description;
|
|
@@ -830,20 +837,25 @@ const handleToolsCall = async (msg, options) => {
|
|
|
830
837
|
content.push(packMsg(output));
|
|
831
838
|
await resp(f.showRes ? `Output: ${output}` : `Status: OK`);
|
|
832
839
|
} catch (err) {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
840
|
+
const rt = `Failed: ${err.message}`;
|
|
841
|
+
content.push(packMsg(rt, true));
|
|
842
|
+
await resp(rt);
|
|
843
|
+
log(rt);
|
|
836
844
|
}
|
|
837
845
|
}
|
|
838
846
|
switch (options?.flavor) {
|
|
839
|
-
case CLAUDE: content = [{ role: user, content }]; break;
|
|
847
|
+
case CLAUDE: content = content.length ? [{ role: user, content }] : []; break;
|
|
840
848
|
case GEMINI: content = [{ role: user, parts: content }]; break;
|
|
841
849
|
}
|
|
842
|
-
await resp(`\n${TOOLS_END}`);
|
|
850
|
+
responded && await resp(`\n${TOOLS_END}`);
|
|
843
851
|
}
|
|
844
|
-
return { toolsResult: [...preRes, ...content], toolsResponse };
|
|
852
|
+
return { toolsResult: [...content.length ? preRes : [], ...content], toolsResponse };
|
|
845
853
|
};
|
|
846
854
|
|
|
855
|
+
const mergeMsgs = (resp, calls) => [resp, ...calls.length ? [
|
|
856
|
+
`⚠️ Tools recursion limit reached: ${MAX_TOOL_RECURSION}`
|
|
857
|
+
] : []].map(x => x.trim()).join('\n\n');
|
|
858
|
+
|
|
847
859
|
const promptChatGPT = async (content, options = {}) => {
|
|
848
860
|
const { client } = await getOpenAIClient(options);
|
|
849
861
|
// https://github.com/openai/openai-node?tab=readme-ov-file#streaming-responses
|
|
@@ -871,10 +883,8 @@ const promptChatGPT = async (content, options = {}) => {
|
|
|
871
883
|
options?.reasoning && !MODELS[options.model]?.reasoning
|
|
872
884
|
), `This model does not support reasoning: ${options.model}`);
|
|
873
885
|
[options.audioMimeType, options.suffix] = [pcm16, 'pcm.wav'];
|
|
874
|
-
let [result, resultAudio,
|
|
875
|
-
|
|
876
|
-
Buffer.alloc(0), null, []
|
|
877
|
-
];
|
|
886
|
+
let [result, resultAudio, event, resultTools, responded]
|
|
887
|
+
= [options?.result ?? '', Buffer.alloc(0), null, [], false];
|
|
878
888
|
const resp = await client.chat.completions.create({
|
|
879
889
|
modalities, audio: options?.audio || (
|
|
880
890
|
modalities?.find?.(x => x === AUDIO)
|
|
@@ -888,53 +898,43 @@ const promptChatGPT = async (content, options = {}) => {
|
|
|
888
898
|
} : {}, model: options.model, stream: true,
|
|
889
899
|
store: true, tool_choice: 'auto',
|
|
890
900
|
});
|
|
891
|
-
for await (
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
901
|
+
for await (event of resp) {
|
|
902
|
+
event = event?.choices?.[0] || {};
|
|
903
|
+
const delta = event.delta || {};
|
|
904
|
+
let deltaText = delta.content || delta.audio?.transcript || '';
|
|
905
|
+
const deltaAudio = delta.audio?.data ? await convert(
|
|
906
|
+
delta.audio.data, { input: BASE64, expected: BUFFER }
|
|
896
907
|
) : Buffer.alloc(0);
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
let curFunc = resultTools.find(z => z.index === deltaFunc[x].index);
|
|
908
|
+
for (const x of delta.tool_calls || []) {
|
|
909
|
+
let curFunc = resultTools.find(y => y.index === x.index);
|
|
900
910
|
curFunc || (resultTools.push(curFunc = {}));
|
|
901
|
-
isSet(
|
|
902
|
-
|
|
903
|
-
|
|
911
|
+
isSet(x.index, true) && (curFunc.index = x.index);
|
|
912
|
+
x.id && (curFunc.id = x.id);
|
|
913
|
+
x.type && (curFunc.type = x.type);
|
|
904
914
|
curFunc.function || (curFunc.function = { name: '', arguments: '' });
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
deltaFunc[x].function.arguments && (curFunc.function.arguments += deltaFunc[x].function.arguments);
|
|
908
|
-
}
|
|
915
|
+
x?.function?.name && (curFunc.function.name += x.function.name);
|
|
916
|
+
x?.function?.arguments && (curFunc.function.arguments += x.function.arguments);
|
|
909
917
|
}
|
|
910
|
-
|
|
918
|
+
deltaText && (responded = responded || (deltaText = `\n\n${deltaText}`));
|
|
911
919
|
result += deltaText;
|
|
912
920
|
resultAudio = Buffer.concat([resultAudio, deltaAudio]);
|
|
913
921
|
const respAudio = options?.delta ? deltaAudio : resultAudio;
|
|
914
|
-
|
|
915
|
-
|
|
922
|
+
await streamResp({
|
|
923
|
+
text: options?.delta ? deltaText : result,
|
|
916
924
|
...respAudio.length ? { audio: { data: respAudio } } : {},
|
|
917
|
-
};
|
|
918
|
-
await ignoreErrFunc(async () => await options?.stream?.(
|
|
919
|
-
await packGptResp(chunk, { ...options, processing: true })
|
|
920
|
-
), LOG);
|
|
925
|
+
}, options);
|
|
921
926
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
content: result, tool_calls: resultTools,
|
|
927
|
+
event = {
|
|
928
|
+
role: assistant, text: result, tool_calls: resultTools,
|
|
925
929
|
...resultAudio.length ? { audio: { data: resultAudio } } : {},
|
|
926
930
|
};
|
|
927
|
-
const { toolsResult, toolsResponse }
|
|
928
|
-
|
|
929
|
-
);
|
|
930
|
-
|
|
931
|
+
const { toolsResult, toolsResponse }
|
|
932
|
+
= await handleToolsCall(event, { ...options, result });
|
|
931
933
|
if (toolsResult.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
932
934
|
return promptChatGPT(content, { ...options, toolsResult, result: toolsResponse });
|
|
933
935
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
] : []].map(x => x.trim()).join('\n\n');
|
|
937
|
-
return await packGptResp(chunk, options);
|
|
936
|
+
event.text = mergeMsgs(toolsResponse, toolsResult);
|
|
937
|
+
return await packGptResp(event, options);
|
|
938
938
|
};
|
|
939
939
|
|
|
940
940
|
const promptAzure = async (content, options = {}) =>
|
|
@@ -966,13 +966,13 @@ const promptOllama = async (content, options = {}) => {
|
|
|
966
966
|
const promptClaude = async (content, options = {}) => {
|
|
967
967
|
const { client } = await getClaudeClient(options);
|
|
968
968
|
options.model = options?.model || DEFAULT_MODELS[CLAUDE];
|
|
969
|
-
const reasoning = options?.reasoning ?? MODELS[options.model]?.reasoning;
|
|
970
969
|
const resp = await client.messages.create({
|
|
971
970
|
model: options.model, max_tokens: MODELS[options.model].maxOutputTokens,
|
|
972
971
|
messages: [
|
|
973
972
|
...options?.messages || [], buildClaudeMessage(content, options),
|
|
974
973
|
...options?.toolsResult || [],
|
|
975
|
-
], stream: true,
|
|
974
|
+
], stream: true,
|
|
975
|
+
...options?.reasoning ?? MODELS[options.model]?.reasoning ? {
|
|
976
976
|
thinking: options?.thinking || { type: 'enabled', budget_tokens: 1024 },
|
|
977
977
|
} : {}, // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
|
|
978
978
|
...MODELS[options.model]?.tools ? {
|
|
@@ -980,57 +980,42 @@ const promptClaude = async (content, options = {}) => {
|
|
|
980
980
|
tool_choice: { type: 'auto' },
|
|
981
981
|
} : {},
|
|
982
982
|
});
|
|
983
|
-
let [event, text, thinking, signature, result, thinkEnd,
|
|
984
|
-
= [null, '', '', '', options?.
|
|
985
|
-
for await (
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
event?.content_block?.text || event?.delta?.text || '',
|
|
989
|
-
];
|
|
983
|
+
let [event, text, thinking, signature, result, thinkEnd, tool_use]
|
|
984
|
+
= [null, '', '', '', options?.result ?? '', '', []];
|
|
985
|
+
for await (const chunk of resp) {
|
|
986
|
+
event = chunk?.content_block || chunk?.delta || {};
|
|
987
|
+
let [thkDelta, txtDelta] = [event.thinking || '', event.text || ''];
|
|
990
988
|
text += txtDelta;
|
|
991
989
|
thinking += thkDelta;
|
|
992
|
-
signature = signature || event?.
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
990
|
+
signature = signature || event?.signature || '';
|
|
991
|
+
thkDelta && thkDelta === thinking
|
|
992
|
+
&& (thkDelta = `${THINK_STR}\n${thkDelta}`);
|
|
993
|
+
thinking && txtDelta && !thinkEnd
|
|
994
|
+
&& (thinkEnd = thkDelta = `${thkDelta}\n${THINK_END}\n\n`);
|
|
995
|
+
if (event?.type === 'tool_use') {
|
|
996
|
+
tool_use.push({ ...event, input: '' });
|
|
997
|
+
} else if (event.partial_json) {
|
|
998
|
+
tool_use[tool_use.length - 1].input += event.partial_json;
|
|
998
999
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
tool_calls[tool_calls.length - 1].input += event?.delta?.partial_json;
|
|
1003
|
-
}
|
|
1004
|
-
const delta = thkDelta + txtDelta;
|
|
1005
|
-
if (delta === '') { continue; }
|
|
1006
|
-
result += delta;
|
|
1007
|
-
event.content = [{ type: TEXT, text: options?.delta ? delta : result }];
|
|
1008
|
-
await ignoreErrFunc(async () => await options?.stream?.(
|
|
1009
|
-
await packGptResp(event, { ...options, processing: true })
|
|
1010
|
-
), LOG);
|
|
1000
|
+
txtDelta = thkDelta + txtDelta;
|
|
1001
|
+
result += txtDelta;
|
|
1002
|
+
await streamResp({ text: options?.delta ? txtDelta : result }, options);
|
|
1011
1003
|
}
|
|
1012
|
-
event
|
|
1013
|
-
|
|
1014
|
-
|
|
1004
|
+
event = {
|
|
1005
|
+
role: assistant, content: [
|
|
1006
|
+
...thinking ? [{ type: THINKING, thinking, signature }] : [],
|
|
1007
|
+
...text ? [{ type: TEXT, text }] : [], ...tool_use,
|
|
1008
|
+
]
|
|
1009
|
+
};
|
|
1015
1010
|
const { toolsResult, toolsResponse } = await handleToolsCall(
|
|
1016
|
-
|
|
1011
|
+
event, { ...options, result, flavor: CLAUDE },
|
|
1017
1012
|
);
|
|
1018
|
-
if (
|
|
1019
|
-
toolsResult[0].content.unshift(
|
|
1020
|
-
...event?.content.filter(x => x?.type !== 'tool_use')
|
|
1021
|
-
);
|
|
1013
|
+
if (tool_use.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
1022
1014
|
return await promptClaude(content, {
|
|
1023
|
-
...options, toolsResult, toolsResponse,
|
|
1015
|
+
...options, toolsResult: [...options?.toolsResult || [], ...toolsResult], result: toolsResponse,
|
|
1024
1016
|
});
|
|
1025
1017
|
}
|
|
1026
|
-
|
|
1027
|
-
textPart.text = [
|
|
1028
|
-
...options?.toolsResponse ? [options?.toolsResponse] : [],
|
|
1029
|
-
textPart.text, ...toolsResult.length ? [
|
|
1030
|
-
`⚠️ Tools recursion limit reached: ${MAX_TOOL_RECURSION}`
|
|
1031
|
-
] : [],
|
|
1032
|
-
].map(x => x.trim()).join('\n\n')
|
|
1033
|
-
return packGptResp(event, options);
|
|
1018
|
+
return packGptResp({ text: mergeMsgs(toolsResponse, tool_use) }, options);
|
|
1034
1019
|
};
|
|
1035
1020
|
|
|
1036
1021
|
const uploadFile = async (input, options) => {
|
package/lib/manifest.mjs
CHANGED