@lvce-editor/chat-view 3.6.0 → 3.8.0
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/chatViewWorkerMain.js +917 -581
- package/package.json +1 -1
|
@@ -1075,6 +1075,7 @@ const create$2 = rpcId => {
|
|
|
1075
1075
|
};
|
|
1076
1076
|
|
|
1077
1077
|
const {
|
|
1078
|
+
invoke: invoke$2,
|
|
1078
1079
|
set: set$3
|
|
1079
1080
|
} = create$2(6002);
|
|
1080
1081
|
|
|
@@ -1525,6 +1526,7 @@ const createDefaultState = () => {
|
|
|
1525
1526
|
tokensUsed: 0,
|
|
1526
1527
|
uid: 0,
|
|
1527
1528
|
usageOverviewEnabled: false,
|
|
1529
|
+
useChatNetworkWorkerForRequests: false,
|
|
1528
1530
|
useMockApi: false,
|
|
1529
1531
|
viewMode: 'list',
|
|
1530
1532
|
warningCount: 0,
|
|
@@ -2790,393 +2792,6 @@ const openRouterTooManyRequestsMessage = 'OpenRouter rate limit reached (429). P
|
|
|
2790
2792
|
const openRouterRequestFailureReasons = ['ContentSecurityPolicyViolation: Check DevTools for details.', 'OpenRouter server offline: Check DevTools for details.', 'Check your internet connection.'];
|
|
2791
2793
|
const openRouterTooManyRequestsReasons = ['Wait a short time and retry your request.', 'Reduce request frequency to avoid rate limits.', 'Use a different model if this one is saturated.'];
|
|
2792
2794
|
|
|
2793
|
-
const delay = async ms => {
|
|
2794
|
-
await new Promise(resolve => setTimeout(resolve, ms));
|
|
2795
|
-
};
|
|
2796
|
-
|
|
2797
|
-
const getMockAiResponse = async (userMessage, delayInMs) => {
|
|
2798
|
-
await delay(delayInMs);
|
|
2799
|
-
return `Mock AI response: I received "${userMessage}".`;
|
|
2800
|
-
};
|
|
2801
|
-
|
|
2802
|
-
let queue = [];
|
|
2803
|
-
let waiters = [];
|
|
2804
|
-
let finished = false;
|
|
2805
|
-
let errorResult;
|
|
2806
|
-
const reset$1 = () => {
|
|
2807
|
-
queue = [];
|
|
2808
|
-
waiters = [];
|
|
2809
|
-
finished = false;
|
|
2810
|
-
errorResult = undefined;
|
|
2811
|
-
};
|
|
2812
|
-
const setHttpErrorResponse = (statusCode, body) => {
|
|
2813
|
-
const rawError = body && typeof body === 'object' ? Reflect.get(body, 'error') : undefined;
|
|
2814
|
-
const errorCode = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'code') : undefined;
|
|
2815
|
-
const errorMessage = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'message') : undefined;
|
|
2816
|
-
const errorType = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'type') : undefined;
|
|
2817
|
-
errorResult = {
|
|
2818
|
-
details: 'http-error',
|
|
2819
|
-
...(typeof errorCode === 'string' ? {
|
|
2820
|
-
errorCode
|
|
2821
|
-
} : {}),
|
|
2822
|
-
...(typeof errorMessage === 'string' ? {
|
|
2823
|
-
errorMessage
|
|
2824
|
-
} : {}),
|
|
2825
|
-
...(typeof errorType === 'string' ? {
|
|
2826
|
-
errorType
|
|
2827
|
-
} : {}),
|
|
2828
|
-
statusCode,
|
|
2829
|
-
type: 'error'
|
|
2830
|
-
};
|
|
2831
|
-
};
|
|
2832
|
-
const takeErrorResponse = () => {
|
|
2833
|
-
const error = errorResult;
|
|
2834
|
-
errorResult = undefined;
|
|
2835
|
-
return error;
|
|
2836
|
-
};
|
|
2837
|
-
const pushChunk = chunk => {
|
|
2838
|
-
if (waiters.length > 0) {
|
|
2839
|
-
const resolve = waiters.shift();
|
|
2840
|
-
resolve?.(chunk);
|
|
2841
|
-
return;
|
|
2842
|
-
}
|
|
2843
|
-
queue.push(chunk);
|
|
2844
|
-
};
|
|
2845
|
-
const finish = () => {
|
|
2846
|
-
finished = true;
|
|
2847
|
-
if (waiters.length === 0) {
|
|
2848
|
-
return;
|
|
2849
|
-
}
|
|
2850
|
-
const activeWaiters = waiters;
|
|
2851
|
-
waiters = [];
|
|
2852
|
-
for (const resolve of activeWaiters) {
|
|
2853
|
-
resolve(undefined);
|
|
2854
|
-
}
|
|
2855
|
-
};
|
|
2856
|
-
const readNextChunk = async () => {
|
|
2857
|
-
if (queue.length > 0) {
|
|
2858
|
-
return queue.shift();
|
|
2859
|
-
}
|
|
2860
|
-
if (finished) {
|
|
2861
|
-
return undefined;
|
|
2862
|
-
}
|
|
2863
|
-
const {
|
|
2864
|
-
promise,
|
|
2865
|
-
resolve
|
|
2866
|
-
} = Promise.withResolvers();
|
|
2867
|
-
waiters.push(resolve);
|
|
2868
|
-
return promise;
|
|
2869
|
-
};
|
|
2870
|
-
|
|
2871
|
-
const parseSseDataLines = eventChunk => {
|
|
2872
|
-
const lines = eventChunk.split('\n');
|
|
2873
|
-
const dataLines = [];
|
|
2874
|
-
for (const line of lines) {
|
|
2875
|
-
if (!line.startsWith('data:')) {
|
|
2876
|
-
continue;
|
|
2877
|
-
}
|
|
2878
|
-
dataLines.push(line.slice(5).trimStart());
|
|
2879
|
-
}
|
|
2880
|
-
return dataLines;
|
|
2881
|
-
};
|
|
2882
|
-
const emitToolCalls = async (toolCallAccumulator, onToolCallsChunk) => {
|
|
2883
|
-
if (!onToolCallsChunk) {
|
|
2884
|
-
return;
|
|
2885
|
-
}
|
|
2886
|
-
const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
|
|
2887
|
-
if (toolCalls.length === 0) {
|
|
2888
|
-
return;
|
|
2889
|
-
}
|
|
2890
|
-
await onToolCallsChunk(toolCalls);
|
|
2891
|
-
};
|
|
2892
|
-
const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
|
|
2893
|
-
const error = takeErrorResponse();
|
|
2894
|
-
if (error) {
|
|
2895
|
-
return error;
|
|
2896
|
-
}
|
|
2897
|
-
let text = '';
|
|
2898
|
-
let remainder = '';
|
|
2899
|
-
let toolCallAccumulator = {};
|
|
2900
|
-
let finishedNotified = false;
|
|
2901
|
-
const notifyFinished = async () => {
|
|
2902
|
-
if (finishedNotified) {
|
|
2903
|
-
return;
|
|
2904
|
-
}
|
|
2905
|
-
finishedNotified = true;
|
|
2906
|
-
if (onEventStreamFinished) {
|
|
2907
|
-
await onEventStreamFinished();
|
|
2908
|
-
}
|
|
2909
|
-
};
|
|
2910
|
-
const handleParsedSseEvent = async parsed => {
|
|
2911
|
-
if (onDataEvent) {
|
|
2912
|
-
await onDataEvent(parsed);
|
|
2913
|
-
}
|
|
2914
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
2915
|
-
return;
|
|
2916
|
-
}
|
|
2917
|
-
const eventType = Reflect.get(parsed, 'type');
|
|
2918
|
-
if (eventType === 'response.completed') {
|
|
2919
|
-
await notifyFinished();
|
|
2920
|
-
return;
|
|
2921
|
-
}
|
|
2922
|
-
if (eventType === 'response.output_text.delta') {
|
|
2923
|
-
const delta = Reflect.get(parsed, 'delta');
|
|
2924
|
-
if (typeof delta !== 'string' || !delta) {
|
|
2925
|
-
return;
|
|
2926
|
-
}
|
|
2927
|
-
text += delta;
|
|
2928
|
-
if (stream && onTextChunk) {
|
|
2929
|
-
await onTextChunk(delta);
|
|
2930
|
-
}
|
|
2931
|
-
return;
|
|
2932
|
-
}
|
|
2933
|
-
if (eventType === 'response.output_item.added') {
|
|
2934
|
-
const outputIndex = Reflect.get(parsed, 'output_index');
|
|
2935
|
-
const item = Reflect.get(parsed, 'item');
|
|
2936
|
-
if (typeof outputIndex !== 'number' || !item || typeof item !== 'object') {
|
|
2937
|
-
return;
|
|
2938
|
-
}
|
|
2939
|
-
if (Reflect.get(item, 'type') !== 'function_call') {
|
|
2940
|
-
return;
|
|
2941
|
-
}
|
|
2942
|
-
const name = Reflect.get(item, 'name');
|
|
2943
|
-
const argumentsValue = Reflect.get(item, 'arguments');
|
|
2944
|
-
const callId = Reflect.get(item, 'call_id');
|
|
2945
|
-
toolCallAccumulator = {
|
|
2946
|
-
...toolCallAccumulator,
|
|
2947
|
-
[outputIndex]: {
|
|
2948
|
-
arguments: typeof argumentsValue === 'string' ? argumentsValue : '',
|
|
2949
|
-
...(typeof callId === 'string' ? {
|
|
2950
|
-
id: callId
|
|
2951
|
-
} : {}),
|
|
2952
|
-
name: typeof name === 'string' ? name : ''
|
|
2953
|
-
}
|
|
2954
|
-
};
|
|
2955
|
-
await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
|
|
2956
|
-
return;
|
|
2957
|
-
}
|
|
2958
|
-
if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
|
|
2959
|
-
const outputIndex = Reflect.get(parsed, 'output_index');
|
|
2960
|
-
if (typeof outputIndex !== 'number') {
|
|
2961
|
-
return;
|
|
2962
|
-
}
|
|
2963
|
-
const current = toolCallAccumulator[outputIndex] || {
|
|
2964
|
-
arguments: '',
|
|
2965
|
-
name: ''
|
|
2966
|
-
};
|
|
2967
|
-
const delta = Reflect.get(parsed, 'delta');
|
|
2968
|
-
const argumentsValue = Reflect.get(parsed, 'arguments');
|
|
2969
|
-
const name = Reflect.get(parsed, 'name');
|
|
2970
|
-
const callId = Reflect.get(parsed, 'call_id');
|
|
2971
|
-
const next = {
|
|
2972
|
-
arguments: typeof argumentsValue === 'string' ? argumentsValue : typeof delta === 'string' ? `${current.arguments}${delta}` : current.arguments,
|
|
2973
|
-
...(typeof callId === 'string' ? {
|
|
2974
|
-
id: callId
|
|
2975
|
-
} : current.id ? {
|
|
2976
|
-
id: current.id
|
|
2977
|
-
} : {}),
|
|
2978
|
-
name: typeof name === 'string' && name ? name : current.name
|
|
2979
|
-
};
|
|
2980
|
-
toolCallAccumulator = {
|
|
2981
|
-
...toolCallAccumulator,
|
|
2982
|
-
[outputIndex]: next
|
|
2983
|
-
};
|
|
2984
|
-
await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
|
|
2985
|
-
}
|
|
2986
|
-
};
|
|
2987
|
-
const consumeSseDataLines = async dataLines => {
|
|
2988
|
-
for (const line of dataLines) {
|
|
2989
|
-
if (line === '[DONE]') {
|
|
2990
|
-
await notifyFinished();
|
|
2991
|
-
continue;
|
|
2992
|
-
}
|
|
2993
|
-
let parsed;
|
|
2994
|
-
try {
|
|
2995
|
-
parsed = JSON.parse(line);
|
|
2996
|
-
} catch {
|
|
2997
|
-
continue;
|
|
2998
|
-
}
|
|
2999
|
-
await handleParsedSseEvent(parsed);
|
|
3000
|
-
}
|
|
3001
|
-
};
|
|
3002
|
-
while (true) {
|
|
3003
|
-
const chunk = await readNextChunk();
|
|
3004
|
-
if (typeof chunk !== 'string') {
|
|
3005
|
-
break;
|
|
3006
|
-
}
|
|
3007
|
-
if (chunk.startsWith('data:')) {
|
|
3008
|
-
remainder += chunk;
|
|
3009
|
-
while (true) {
|
|
3010
|
-
const separatorIndex = remainder.indexOf('\n\n');
|
|
3011
|
-
if (separatorIndex === -1) {
|
|
3012
|
-
break;
|
|
3013
|
-
}
|
|
3014
|
-
const rawEvent = remainder.slice(0, separatorIndex);
|
|
3015
|
-
remainder = remainder.slice(separatorIndex + 2);
|
|
3016
|
-
const dataLines = parseSseDataLines(rawEvent);
|
|
3017
|
-
await consumeSseDataLines(dataLines);
|
|
3018
|
-
}
|
|
3019
|
-
continue;
|
|
3020
|
-
}
|
|
3021
|
-
text += chunk;
|
|
3022
|
-
if (stream && onTextChunk) {
|
|
3023
|
-
await onTextChunk(chunk);
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
if (remainder) {
|
|
3027
|
-
const dataLines = parseSseDataLines(remainder);
|
|
3028
|
-
await consumeSseDataLines(dataLines);
|
|
3029
|
-
}
|
|
3030
|
-
await notifyFinished();
|
|
3031
|
-
return {
|
|
3032
|
-
text,
|
|
3033
|
-
type: 'success'
|
|
3034
|
-
};
|
|
3035
|
-
};
|
|
3036
|
-
|
|
3037
|
-
const activateByEvent = (event, assetDir, platform) => {
|
|
3038
|
-
// @ts-ignore
|
|
3039
|
-
return activateByEvent$1(event, assetDir, platform);
|
|
3040
|
-
};
|
|
3041
|
-
|
|
3042
|
-
const executeProvider = async ({
|
|
3043
|
-
assetDir,
|
|
3044
|
-
event,
|
|
3045
|
-
method,
|
|
3046
|
-
noProviderFoundMessage,
|
|
3047
|
-
params,
|
|
3048
|
-
platform
|
|
3049
|
-
}) => {
|
|
3050
|
-
await activateByEvent(event, assetDir, platform);
|
|
3051
|
-
// @ts-ignore
|
|
3052
|
-
const result = invoke$1(method, ...params);
|
|
3053
|
-
return result;
|
|
3054
|
-
};
|
|
3055
|
-
|
|
3056
|
-
const CommandExecute = 'ExtensionHostCommand.executeCommand';
|
|
3057
|
-
const FileSystemWriteFile = 'ExtensionHostFileSystem.writeFile';
|
|
3058
|
-
|
|
3059
|
-
const normalizeLimitInfo = value => {
|
|
3060
|
-
if (!value || typeof value !== 'object') {
|
|
3061
|
-
return undefined;
|
|
3062
|
-
}
|
|
3063
|
-
const limitRemaining = Reflect.get(value, 'limitRemaining');
|
|
3064
|
-
const limitReset = Reflect.get(value, 'limitReset');
|
|
3065
|
-
const retryAfter = Reflect.get(value, 'retryAfter');
|
|
3066
|
-
const usage = Reflect.get(value, 'usage');
|
|
3067
|
-
const usageDaily = Reflect.get(value, 'usageDaily');
|
|
3068
|
-
const normalized = {
|
|
3069
|
-
...(typeof limitRemaining === 'number' || limitRemaining === null ? {
|
|
3070
|
-
limitRemaining
|
|
3071
|
-
} : {}),
|
|
3072
|
-
...(typeof limitReset === 'string' || limitReset === null ? {
|
|
3073
|
-
limitReset
|
|
3074
|
-
} : {}),
|
|
3075
|
-
...(typeof retryAfter === 'string' || retryAfter === null ? {
|
|
3076
|
-
retryAfter
|
|
3077
|
-
} : {}),
|
|
3078
|
-
...(typeof usage === 'number' ? {
|
|
3079
|
-
usage
|
|
3080
|
-
} : {}),
|
|
3081
|
-
...(typeof usageDaily === 'number' ? {
|
|
3082
|
-
usageDaily
|
|
3083
|
-
} : {})
|
|
3084
|
-
};
|
|
3085
|
-
const hasDetails = typeof limitRemaining === 'number' || limitRemaining === null || typeof limitReset === 'string' || limitReset === null || typeof retryAfter === 'string' || retryAfter === null || typeof usage === 'number' || typeof usageDaily === 'number';
|
|
3086
|
-
return hasDetails ? normalized : undefined;
|
|
3087
|
-
};
|
|
3088
|
-
|
|
3089
|
-
const normalizeMockResult = value => {
|
|
3090
|
-
if (typeof value === 'string') {
|
|
3091
|
-
return {
|
|
3092
|
-
text: value,
|
|
3093
|
-
type: 'success'
|
|
3094
|
-
};
|
|
3095
|
-
}
|
|
3096
|
-
if (!value || typeof value !== 'object') {
|
|
3097
|
-
return {
|
|
3098
|
-
details: 'request-failed',
|
|
3099
|
-
type: 'error'
|
|
3100
|
-
};
|
|
3101
|
-
}
|
|
3102
|
-
const type = Reflect.get(value, 'type');
|
|
3103
|
-
if (type === 'success') {
|
|
3104
|
-
const text = Reflect.get(value, 'text');
|
|
3105
|
-
if (typeof text === 'string') {
|
|
3106
|
-
return {
|
|
3107
|
-
text,
|
|
3108
|
-
type: 'success'
|
|
3109
|
-
};
|
|
3110
|
-
}
|
|
3111
|
-
return {
|
|
3112
|
-
details: 'request-failed',
|
|
3113
|
-
type: 'error'
|
|
3114
|
-
};
|
|
3115
|
-
}
|
|
3116
|
-
if (type === 'error') {
|
|
3117
|
-
const details = Reflect.get(value, 'details');
|
|
3118
|
-
if (details === 'request-failed' || details === 'too-many-requests' || details === 'http-error') {
|
|
3119
|
-
const rawMessage = Reflect.get(value, 'rawMessage');
|
|
3120
|
-
const statusCode = Reflect.get(value, 'statusCode');
|
|
3121
|
-
const limitInfo = normalizeLimitInfo(Reflect.get(value, 'limitInfo'));
|
|
3122
|
-
return {
|
|
3123
|
-
details,
|
|
3124
|
-
...(limitInfo ? {
|
|
3125
|
-
limitInfo
|
|
3126
|
-
} : {}),
|
|
3127
|
-
...(typeof rawMessage === 'string' ? {
|
|
3128
|
-
rawMessage
|
|
3129
|
-
} : {}),
|
|
3130
|
-
...(typeof statusCode === 'number' ? {
|
|
3131
|
-
statusCode
|
|
3132
|
-
} : {}),
|
|
3133
|
-
type: 'error'
|
|
3134
|
-
};
|
|
3135
|
-
}
|
|
3136
|
-
}
|
|
3137
|
-
const text = Reflect.get(value, 'text');
|
|
3138
|
-
if (typeof text === 'string') {
|
|
3139
|
-
return {
|
|
3140
|
-
text,
|
|
3141
|
-
type: 'success'
|
|
3142
|
-
};
|
|
3143
|
-
}
|
|
3144
|
-
return {
|
|
3145
|
-
details: 'request-failed',
|
|
3146
|
-
type: 'error'
|
|
3147
|
-
};
|
|
3148
|
-
};
|
|
3149
|
-
|
|
3150
|
-
const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform) => {
|
|
3151
|
-
if (!mockApiCommandId) {
|
|
3152
|
-
return {
|
|
3153
|
-
details: 'request-failed',
|
|
3154
|
-
type: 'error'
|
|
3155
|
-
};
|
|
3156
|
-
}
|
|
3157
|
-
try {
|
|
3158
|
-
const result = await executeProvider({
|
|
3159
|
-
assetDir,
|
|
3160
|
-
event: `onCommand:${mockApiCommandId}`,
|
|
3161
|
-
method: CommandExecute,
|
|
3162
|
-
noProviderFoundMessage: 'No mock api command found',
|
|
3163
|
-
params: [mockApiCommandId, {
|
|
3164
|
-
messages,
|
|
3165
|
-
modelId,
|
|
3166
|
-
openRouterApiBaseUrl,
|
|
3167
|
-
openRouterApiKey
|
|
3168
|
-
}],
|
|
3169
|
-
platform
|
|
3170
|
-
});
|
|
3171
|
-
return normalizeMockResult(result);
|
|
3172
|
-
} catch {
|
|
3173
|
-
return {
|
|
3174
|
-
details: 'request-failed',
|
|
3175
|
-
type: 'error'
|
|
3176
|
-
};
|
|
3177
|
-
}
|
|
3178
|
-
};
|
|
3179
|
-
|
|
3180
2795
|
const executeGetWorkspaceUriTool = async (_args, _options) => {
|
|
3181
2796
|
try {
|
|
3182
2797
|
const workspaceUri = await getWorkspacePath();
|
|
@@ -3307,6 +2922,25 @@ const executeRenderHtmlTool = async (args, _options) => {
|
|
|
3307
2922
|
});
|
|
3308
2923
|
};
|
|
3309
2924
|
|
|
2925
|
+
const activateByEvent = (event, assetDir, platform) => {
|
|
2926
|
+
// @ts-ignore
|
|
2927
|
+
return activateByEvent$1(event, assetDir, platform);
|
|
2928
|
+
};
|
|
2929
|
+
|
|
2930
|
+
const executeProvider = async ({
|
|
2931
|
+
assetDir,
|
|
2932
|
+
event,
|
|
2933
|
+
method,
|
|
2934
|
+
noProviderFoundMessage,
|
|
2935
|
+
params,
|
|
2936
|
+
platform
|
|
2937
|
+
}) => {
|
|
2938
|
+
await activateByEvent(event, assetDir, platform);
|
|
2939
|
+
// @ts-ignore
|
|
2940
|
+
const result = invoke$1(method, ...params);
|
|
2941
|
+
return result;
|
|
2942
|
+
};
|
|
2943
|
+
|
|
3310
2944
|
const OnFileSystem = 'onFileSystem';
|
|
3311
2945
|
|
|
3312
2946
|
const executeFileSystemCommand = async (method, params, options) => {
|
|
@@ -3320,6 +2954,9 @@ const executeFileSystemCommand = async (method, params, options) => {
|
|
|
3320
2954
|
});
|
|
3321
2955
|
};
|
|
3322
2956
|
|
|
2957
|
+
const CommandExecute = 'ExtensionHostCommand.executeCommand';
|
|
2958
|
+
const FileSystemWriteFile = 'ExtensionHostFileSystem.writeFile';
|
|
2959
|
+
|
|
3323
2960
|
const executeWriteFileTool = async (args, options) => {
|
|
3324
2961
|
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3325
2962
|
const content = typeof args.content === 'string' ? args.content : '';
|
|
@@ -3379,17 +3016,61 @@ const executeChatTool = async (name, rawArguments, options) => {
|
|
|
3379
3016
|
error: `Unknown tool: ${name}`
|
|
3380
3017
|
});
|
|
3381
3018
|
};
|
|
3382
|
-
|
|
3383
|
-
const getReadFileTool = () => {
|
|
3019
|
+
|
|
3020
|
+
const getReadFileTool = () => {
|
|
3021
|
+
return {
|
|
3022
|
+
function: {
|
|
3023
|
+
description: 'Read UTF-8 text content from a file inside the currently open workspace folder. Only pass an absolute URI.',
|
|
3024
|
+
name: 'read_file',
|
|
3025
|
+
parameters: {
|
|
3026
|
+
additionalProperties: false,
|
|
3027
|
+
properties: {
|
|
3028
|
+
uri: {
|
|
3029
|
+
description: 'Absolute file URI within the workspace (for example: file:///workspace/src/index.ts).',
|
|
3030
|
+
type: 'string'
|
|
3031
|
+
}
|
|
3032
|
+
},
|
|
3033
|
+
required: ['uri'],
|
|
3034
|
+
type: 'object'
|
|
3035
|
+
}
|
|
3036
|
+
},
|
|
3037
|
+
type: 'function'
|
|
3038
|
+
};
|
|
3039
|
+
};
|
|
3040
|
+
const getWriteFileTool = () => {
|
|
3041
|
+
return {
|
|
3042
|
+
function: {
|
|
3043
|
+
description: 'Write UTF-8 text content to a file inside the currently open workspace folder.',
|
|
3044
|
+
name: 'write_file',
|
|
3045
|
+
parameters: {
|
|
3046
|
+
additionalProperties: false,
|
|
3047
|
+
properties: {
|
|
3048
|
+
content: {
|
|
3049
|
+
description: 'New UTF-8 text content to write to the file.',
|
|
3050
|
+
type: 'string'
|
|
3051
|
+
},
|
|
3052
|
+
path: {
|
|
3053
|
+
description: 'Relative file path within the workspace (for example: src/index.ts).',
|
|
3054
|
+
type: 'string'
|
|
3055
|
+
}
|
|
3056
|
+
},
|
|
3057
|
+
required: ['path', 'content'],
|
|
3058
|
+
type: 'object'
|
|
3059
|
+
}
|
|
3060
|
+
},
|
|
3061
|
+
type: 'function'
|
|
3062
|
+
};
|
|
3063
|
+
};
|
|
3064
|
+
const getListFilesTool = () => {
|
|
3384
3065
|
return {
|
|
3385
3066
|
function: {
|
|
3386
|
-
description: '
|
|
3387
|
-
name: '
|
|
3067
|
+
description: 'List direct children (files and folders) for a folder URI inside the currently open workspace folder. Only pass an absolute URI.',
|
|
3068
|
+
name: 'list_files',
|
|
3388
3069
|
parameters: {
|
|
3389
3070
|
additionalProperties: false,
|
|
3390
3071
|
properties: {
|
|
3391
3072
|
uri: {
|
|
3392
|
-
description: 'Absolute
|
|
3073
|
+
description: 'Absolute folder URI within the workspace (for example: file:///workspace/src).',
|
|
3393
3074
|
type: 'string'
|
|
3394
3075
|
}
|
|
3395
3076
|
},
|
|
@@ -3400,106 +3081,434 @@ const getReadFileTool = () => {
|
|
|
3400
3081
|
type: 'function'
|
|
3401
3082
|
};
|
|
3402
3083
|
};
|
|
3403
|
-
const
|
|
3084
|
+
const getGetWorkspaceUriTool = () => {
|
|
3404
3085
|
return {
|
|
3405
3086
|
function: {
|
|
3406
|
-
description: '
|
|
3407
|
-
name: '
|
|
3087
|
+
description: 'Get the URI of the currently open workspace folder.',
|
|
3088
|
+
name: 'getWorkspaceUri',
|
|
3089
|
+
parameters: {
|
|
3090
|
+
additionalProperties: false,
|
|
3091
|
+
properties: {},
|
|
3092
|
+
type: 'object'
|
|
3093
|
+
}
|
|
3094
|
+
},
|
|
3095
|
+
type: 'function'
|
|
3096
|
+
};
|
|
3097
|
+
};
|
|
3098
|
+
const getRenderHtmlTool = () => {
|
|
3099
|
+
return {
|
|
3100
|
+
function: {
|
|
3101
|
+
description: 'Render custom HTML and optional CSS directly in the chat tool call list using native chat UI rendering. Use this for structured cards, tables, and small dashboards. After calling this tool, do not repeat the same HTML, data table, or long content again as plain text unless the user explicitly asks for a text-only version.',
|
|
3102
|
+
name: 'render_html',
|
|
3408
3103
|
parameters: {
|
|
3409
3104
|
additionalProperties: false,
|
|
3410
3105
|
properties: {
|
|
3411
|
-
|
|
3412
|
-
description: '
|
|
3106
|
+
css: {
|
|
3107
|
+
description: 'Optional CSS string applied inside the preview document.',
|
|
3413
3108
|
type: 'string'
|
|
3414
3109
|
},
|
|
3415
|
-
|
|
3416
|
-
description: '
|
|
3110
|
+
html: {
|
|
3111
|
+
description: 'HTML string to render in the preview document.',
|
|
3112
|
+
type: 'string'
|
|
3113
|
+
},
|
|
3114
|
+
title: {
|
|
3115
|
+
description: 'Optional short title for the preview.',
|
|
3417
3116
|
type: 'string'
|
|
3418
3117
|
}
|
|
3419
3118
|
},
|
|
3420
|
-
required: ['
|
|
3119
|
+
required: ['html'],
|
|
3421
3120
|
type: 'object'
|
|
3422
3121
|
}
|
|
3423
3122
|
},
|
|
3424
3123
|
type: 'function'
|
|
3425
3124
|
};
|
|
3426
|
-
};
|
|
3427
|
-
const
|
|
3428
|
-
return
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3125
|
+
};
|
|
3126
|
+
const getBasicChatTools = () => {
|
|
3127
|
+
return [getReadFileTool(), getWriteFileTool(), getListFilesTool(), getGetWorkspaceUriTool(), getRenderHtmlTool()];
|
|
3128
|
+
};
|
|
3129
|
+
|
|
3130
|
+
const getClientRequestIdHeader = () => {
|
|
3131
|
+
return {
|
|
3132
|
+
'x-client-request-id': crypto.randomUUID()
|
|
3133
|
+
};
|
|
3134
|
+
};
|
|
3135
|
+
|
|
3136
|
+
const delay = async ms => {
|
|
3137
|
+
await new Promise(resolve => setTimeout(resolve, ms));
|
|
3138
|
+
};
|
|
3139
|
+
|
|
3140
|
+
const getMockAiResponse = async (userMessage, delayInMs) => {
|
|
3141
|
+
await delay(delayInMs);
|
|
3142
|
+
return `Mock AI response: I received "${userMessage}".`;
|
|
3143
|
+
};
|
|
3144
|
+
|
|
3145
|
+
let queue = [];
|
|
3146
|
+
let waiters = [];
|
|
3147
|
+
let finished = false;
|
|
3148
|
+
let errorResult;
|
|
3149
|
+
const reset$2 = () => {
|
|
3150
|
+
queue = [];
|
|
3151
|
+
waiters = [];
|
|
3152
|
+
finished = false;
|
|
3153
|
+
errorResult = undefined;
|
|
3154
|
+
};
|
|
3155
|
+
const setHttpErrorResponse = (statusCode, body) => {
|
|
3156
|
+
const rawError = body && typeof body === 'object' ? Reflect.get(body, 'error') : undefined;
|
|
3157
|
+
const errorCode = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'code') : undefined;
|
|
3158
|
+
const errorMessage = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'message') : undefined;
|
|
3159
|
+
const errorType = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'type') : undefined;
|
|
3160
|
+
errorResult = {
|
|
3161
|
+
details: 'http-error',
|
|
3162
|
+
...(typeof errorCode === 'string' ? {
|
|
3163
|
+
errorCode
|
|
3164
|
+
} : {}),
|
|
3165
|
+
...(typeof errorMessage === 'string' ? {
|
|
3166
|
+
errorMessage
|
|
3167
|
+
} : {}),
|
|
3168
|
+
...(typeof errorType === 'string' ? {
|
|
3169
|
+
errorType
|
|
3170
|
+
} : {}),
|
|
3171
|
+
statusCode,
|
|
3172
|
+
type: 'error'
|
|
3173
|
+
};
|
|
3174
|
+
};
|
|
3175
|
+
const takeErrorResponse = () => {
|
|
3176
|
+
const error = errorResult;
|
|
3177
|
+
errorResult = undefined;
|
|
3178
|
+
return error;
|
|
3179
|
+
};
|
|
3180
|
+
const pushChunk = chunk => {
|
|
3181
|
+
if (waiters.length > 0) {
|
|
3182
|
+
const resolve = waiters.shift();
|
|
3183
|
+
resolve?.(chunk);
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
queue.push(chunk);
|
|
3187
|
+
};
|
|
3188
|
+
const finish = () => {
|
|
3189
|
+
finished = true;
|
|
3190
|
+
if (waiters.length === 0) {
|
|
3191
|
+
return;
|
|
3192
|
+
}
|
|
3193
|
+
const activeWaiters = waiters;
|
|
3194
|
+
waiters = [];
|
|
3195
|
+
for (const resolve of activeWaiters) {
|
|
3196
|
+
resolve(undefined);
|
|
3197
|
+
}
|
|
3198
|
+
};
|
|
3199
|
+
const readNextChunk = async () => {
|
|
3200
|
+
if (queue.length > 0) {
|
|
3201
|
+
return queue.shift();
|
|
3202
|
+
}
|
|
3203
|
+
if (finished) {
|
|
3204
|
+
return undefined;
|
|
3205
|
+
}
|
|
3206
|
+
const {
|
|
3207
|
+
promise,
|
|
3208
|
+
resolve
|
|
3209
|
+
} = Promise.withResolvers();
|
|
3210
|
+
waiters.push(resolve);
|
|
3211
|
+
return promise;
|
|
3212
|
+
};
|
|
3213
|
+
|
|
3214
|
+
const parseSseDataLines = eventChunk => {
|
|
3215
|
+
const lines = eventChunk.split('\n');
|
|
3216
|
+
const dataLines = [];
|
|
3217
|
+
for (const line of lines) {
|
|
3218
|
+
if (!line.startsWith('data:')) {
|
|
3219
|
+
continue;
|
|
3220
|
+
}
|
|
3221
|
+
dataLines.push(line.slice(5).trimStart());
|
|
3222
|
+
}
|
|
3223
|
+
return dataLines;
|
|
3224
|
+
};
|
|
3225
|
+
const emitToolCalls = async (toolCallAccumulator, onToolCallsChunk) => {
|
|
3226
|
+
if (!onToolCallsChunk) {
|
|
3227
|
+
return;
|
|
3228
|
+
}
|
|
3229
|
+
const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
|
|
3230
|
+
if (toolCalls.length === 0) {
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
await onToolCallsChunk(toolCalls);
|
|
3234
|
+
};
|
|
3235
|
+
const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
|
|
3236
|
+
const error = takeErrorResponse();
|
|
3237
|
+
if (error) {
|
|
3238
|
+
return error;
|
|
3239
|
+
}
|
|
3240
|
+
let text = '';
|
|
3241
|
+
let remainder = '';
|
|
3242
|
+
let toolCallAccumulator = {};
|
|
3243
|
+
let finishedNotified = false;
|
|
3244
|
+
const notifyFinished = async () => {
|
|
3245
|
+
if (finishedNotified) {
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
finishedNotified = true;
|
|
3249
|
+
if (onEventStreamFinished) {
|
|
3250
|
+
await onEventStreamFinished();
|
|
3251
|
+
}
|
|
3252
|
+
};
|
|
3253
|
+
const handleParsedSseEvent = async parsed => {
|
|
3254
|
+
if (onDataEvent) {
|
|
3255
|
+
await onDataEvent(parsed);
|
|
3256
|
+
}
|
|
3257
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
3258
|
+
return;
|
|
3259
|
+
}
|
|
3260
|
+
const eventType = Reflect.get(parsed, 'type');
|
|
3261
|
+
if (eventType === 'response.completed') {
|
|
3262
|
+
await notifyFinished();
|
|
3263
|
+
return;
|
|
3264
|
+
}
|
|
3265
|
+
if (eventType === 'response.output_text.delta') {
|
|
3266
|
+
const delta = Reflect.get(parsed, 'delta');
|
|
3267
|
+
if (typeof delta !== 'string' || !delta) {
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
text += delta;
|
|
3271
|
+
if (stream && onTextChunk) {
|
|
3272
|
+
await onTextChunk(delta);
|
|
3273
|
+
}
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
if (eventType === 'response.output_item.added') {
|
|
3277
|
+
const outputIndex = Reflect.get(parsed, 'output_index');
|
|
3278
|
+
const item = Reflect.get(parsed, 'item');
|
|
3279
|
+
if (typeof outputIndex !== 'number' || !item || typeof item !== 'object') {
|
|
3280
|
+
return;
|
|
3281
|
+
}
|
|
3282
|
+
if (Reflect.get(item, 'type') !== 'function_call') {
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
const name = Reflect.get(item, 'name');
|
|
3286
|
+
const argumentsValue = Reflect.get(item, 'arguments');
|
|
3287
|
+
const callId = Reflect.get(item, 'call_id');
|
|
3288
|
+
toolCallAccumulator = {
|
|
3289
|
+
...toolCallAccumulator,
|
|
3290
|
+
[outputIndex]: {
|
|
3291
|
+
arguments: typeof argumentsValue === 'string' ? argumentsValue : '',
|
|
3292
|
+
...(typeof callId === 'string' ? {
|
|
3293
|
+
id: callId
|
|
3294
|
+
} : {}),
|
|
3295
|
+
name: typeof name === 'string' ? name : ''
|
|
3296
|
+
}
|
|
3297
|
+
};
|
|
3298
|
+
await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
|
|
3299
|
+
return;
|
|
3300
|
+
}
|
|
3301
|
+
if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
|
|
3302
|
+
const outputIndex = Reflect.get(parsed, 'output_index');
|
|
3303
|
+
if (typeof outputIndex !== 'number') {
|
|
3304
|
+
return;
|
|
3305
|
+
}
|
|
3306
|
+
const current = toolCallAccumulator[outputIndex] || {
|
|
3307
|
+
arguments: '',
|
|
3308
|
+
name: ''
|
|
3309
|
+
};
|
|
3310
|
+
const delta = Reflect.get(parsed, 'delta');
|
|
3311
|
+
const argumentsValue = Reflect.get(parsed, 'arguments');
|
|
3312
|
+
const name = Reflect.get(parsed, 'name');
|
|
3313
|
+
const callId = Reflect.get(parsed, 'call_id');
|
|
3314
|
+
const next = {
|
|
3315
|
+
arguments: typeof argumentsValue === 'string' ? argumentsValue : typeof delta === 'string' ? `${current.arguments}${delta}` : current.arguments,
|
|
3316
|
+
...(typeof callId === 'string' ? {
|
|
3317
|
+
id: callId
|
|
3318
|
+
} : current.id ? {
|
|
3319
|
+
id: current.id
|
|
3320
|
+
} : {}),
|
|
3321
|
+
name: typeof name === 'string' && name ? name : current.name
|
|
3322
|
+
};
|
|
3323
|
+
toolCallAccumulator = {
|
|
3324
|
+
...toolCallAccumulator,
|
|
3325
|
+
[outputIndex]: next
|
|
3326
|
+
};
|
|
3327
|
+
await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
|
|
3328
|
+
}
|
|
3329
|
+
};
|
|
3330
|
+
const consumeSseDataLines = async dataLines => {
|
|
3331
|
+
for (const line of dataLines) {
|
|
3332
|
+
if (line === '[DONE]') {
|
|
3333
|
+
await notifyFinished();
|
|
3334
|
+
continue;
|
|
3442
3335
|
}
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
}
|
|
3447
|
-
|
|
3448
|
-
return {
|
|
3449
|
-
function: {
|
|
3450
|
-
description: 'Get the URI of the currently open workspace folder.',
|
|
3451
|
-
name: 'getWorkspaceUri',
|
|
3452
|
-
parameters: {
|
|
3453
|
-
additionalProperties: false,
|
|
3454
|
-
properties: {},
|
|
3455
|
-
type: 'object'
|
|
3336
|
+
let parsed;
|
|
3337
|
+
try {
|
|
3338
|
+
parsed = JSON.parse(line);
|
|
3339
|
+
} catch {
|
|
3340
|
+
continue;
|
|
3456
3341
|
}
|
|
3457
|
-
|
|
3458
|
-
|
|
3342
|
+
await handleParsedSseEvent(parsed);
|
|
3343
|
+
}
|
|
3459
3344
|
};
|
|
3460
|
-
|
|
3461
|
-
const
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
},
|
|
3477
|
-
title: {
|
|
3478
|
-
description: 'Optional short title for the preview.',
|
|
3479
|
-
type: 'string'
|
|
3480
|
-
}
|
|
3481
|
-
},
|
|
3482
|
-
required: ['html'],
|
|
3483
|
-
type: 'object'
|
|
3345
|
+
while (true) {
|
|
3346
|
+
const chunk = await readNextChunk();
|
|
3347
|
+
if (typeof chunk !== 'string') {
|
|
3348
|
+
break;
|
|
3349
|
+
}
|
|
3350
|
+
if (chunk.startsWith('data:')) {
|
|
3351
|
+
remainder += chunk;
|
|
3352
|
+
while (true) {
|
|
3353
|
+
const separatorIndex = remainder.indexOf('\n\n');
|
|
3354
|
+
if (separatorIndex === -1) {
|
|
3355
|
+
break;
|
|
3356
|
+
}
|
|
3357
|
+
const rawEvent = remainder.slice(0, separatorIndex);
|
|
3358
|
+
remainder = remainder.slice(separatorIndex + 2);
|
|
3359
|
+
const dataLines = parseSseDataLines(rawEvent);
|
|
3360
|
+
await consumeSseDataLines(dataLines);
|
|
3484
3361
|
}
|
|
3485
|
-
|
|
3486
|
-
|
|
3362
|
+
continue;
|
|
3363
|
+
}
|
|
3364
|
+
text += chunk;
|
|
3365
|
+
if (stream && onTextChunk) {
|
|
3366
|
+
await onTextChunk(chunk);
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
if (remainder) {
|
|
3370
|
+
const dataLines = parseSseDataLines(remainder);
|
|
3371
|
+
await consumeSseDataLines(dataLines);
|
|
3372
|
+
}
|
|
3373
|
+
await notifyFinished();
|
|
3374
|
+
return {
|
|
3375
|
+
text,
|
|
3376
|
+
type: 'success'
|
|
3487
3377
|
};
|
|
3488
3378
|
};
|
|
3489
|
-
|
|
3490
|
-
|
|
3379
|
+
|
|
3380
|
+
const normalizeLimitInfo = value => {
|
|
3381
|
+
if (!value || typeof value !== 'object') {
|
|
3382
|
+
return undefined;
|
|
3383
|
+
}
|
|
3384
|
+
const limitRemaining = Reflect.get(value, 'limitRemaining');
|
|
3385
|
+
const limitReset = Reflect.get(value, 'limitReset');
|
|
3386
|
+
const retryAfter = Reflect.get(value, 'retryAfter');
|
|
3387
|
+
const usage = Reflect.get(value, 'usage');
|
|
3388
|
+
const usageDaily = Reflect.get(value, 'usageDaily');
|
|
3389
|
+
const normalized = {
|
|
3390
|
+
...(typeof limitRemaining === 'number' || limitRemaining === null ? {
|
|
3391
|
+
limitRemaining
|
|
3392
|
+
} : {}),
|
|
3393
|
+
...(typeof limitReset === 'string' || limitReset === null ? {
|
|
3394
|
+
limitReset
|
|
3395
|
+
} : {}),
|
|
3396
|
+
...(typeof retryAfter === 'string' || retryAfter === null ? {
|
|
3397
|
+
retryAfter
|
|
3398
|
+
} : {}),
|
|
3399
|
+
...(typeof usage === 'number' ? {
|
|
3400
|
+
usage
|
|
3401
|
+
} : {}),
|
|
3402
|
+
...(typeof usageDaily === 'number' ? {
|
|
3403
|
+
usageDaily
|
|
3404
|
+
} : {})
|
|
3405
|
+
};
|
|
3406
|
+
const hasDetails = typeof limitRemaining === 'number' || limitRemaining === null || typeof limitReset === 'string' || limitReset === null || typeof retryAfter === 'string' || retryAfter === null || typeof usage === 'number' || typeof usageDaily === 'number';
|
|
3407
|
+
return hasDetails ? normalized : undefined;
|
|
3491
3408
|
};
|
|
3492
3409
|
|
|
3493
|
-
const
|
|
3410
|
+
const normalizeMockResult = value => {
|
|
3411
|
+
if (typeof value === 'string') {
|
|
3412
|
+
return {
|
|
3413
|
+
text: value,
|
|
3414
|
+
type: 'success'
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
if (!value || typeof value !== 'object') {
|
|
3418
|
+
return {
|
|
3419
|
+
details: 'request-failed',
|
|
3420
|
+
type: 'error'
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
const type = Reflect.get(value, 'type');
|
|
3424
|
+
if (type === 'success') {
|
|
3425
|
+
const text = Reflect.get(value, 'text');
|
|
3426
|
+
if (typeof text === 'string') {
|
|
3427
|
+
return {
|
|
3428
|
+
text,
|
|
3429
|
+
type: 'success'
|
|
3430
|
+
};
|
|
3431
|
+
}
|
|
3432
|
+
return {
|
|
3433
|
+
details: 'request-failed',
|
|
3434
|
+
type: 'error'
|
|
3435
|
+
};
|
|
3436
|
+
}
|
|
3437
|
+
if (type === 'error') {
|
|
3438
|
+
const details = Reflect.get(value, 'details');
|
|
3439
|
+
if (details === 'request-failed' || details === 'too-many-requests' || details === 'http-error') {
|
|
3440
|
+
const rawMessage = Reflect.get(value, 'rawMessage');
|
|
3441
|
+
const statusCode = Reflect.get(value, 'statusCode');
|
|
3442
|
+
const limitInfo = normalizeLimitInfo(Reflect.get(value, 'limitInfo'));
|
|
3443
|
+
return {
|
|
3444
|
+
details,
|
|
3445
|
+
...(limitInfo ? {
|
|
3446
|
+
limitInfo
|
|
3447
|
+
} : {}),
|
|
3448
|
+
...(typeof rawMessage === 'string' ? {
|
|
3449
|
+
rawMessage
|
|
3450
|
+
} : {}),
|
|
3451
|
+
...(typeof statusCode === 'number' ? {
|
|
3452
|
+
statusCode
|
|
3453
|
+
} : {}),
|
|
3454
|
+
type: 'error'
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
const text = Reflect.get(value, 'text');
|
|
3459
|
+
if (typeof text === 'string') {
|
|
3460
|
+
return {
|
|
3461
|
+
text,
|
|
3462
|
+
type: 'success'
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3494
3465
|
return {
|
|
3495
|
-
'
|
|
3466
|
+
details: 'request-failed',
|
|
3467
|
+
type: 'error'
|
|
3496
3468
|
};
|
|
3497
3469
|
};
|
|
3498
3470
|
|
|
3471
|
+
const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform) => {
|
|
3472
|
+
if (!mockApiCommandId) {
|
|
3473
|
+
return {
|
|
3474
|
+
details: 'request-failed',
|
|
3475
|
+
type: 'error'
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
try {
|
|
3479
|
+
const result = await executeProvider({
|
|
3480
|
+
assetDir,
|
|
3481
|
+
event: `onCommand:${mockApiCommandId}`,
|
|
3482
|
+
method: CommandExecute,
|
|
3483
|
+
noProviderFoundMessage: 'No mock api command found',
|
|
3484
|
+
params: [mockApiCommandId, {
|
|
3485
|
+
messages,
|
|
3486
|
+
modelId,
|
|
3487
|
+
openRouterApiBaseUrl,
|
|
3488
|
+
openRouterApiKey
|
|
3489
|
+
}],
|
|
3490
|
+
platform
|
|
3491
|
+
});
|
|
3492
|
+
return normalizeMockResult(result);
|
|
3493
|
+
} catch {
|
|
3494
|
+
return {
|
|
3495
|
+
details: 'request-failed',
|
|
3496
|
+
type: 'error'
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
|
|
3499
3501
|
const getOpenApiApiEndpoint = openApiApiBaseUrl => {
|
|
3500
3502
|
return `${openApiApiBaseUrl}/responses`;
|
|
3501
3503
|
};
|
|
3502
3504
|
|
|
3505
|
+
const makeApiRequest = async options => {
|
|
3506
|
+
return invoke$2('ChatNetwork.makeApiRequest', options);
|
|
3507
|
+
};
|
|
3508
|
+
const makeStreamingApiRequest = async options => {
|
|
3509
|
+
return invoke$2('ChatNetwork.makeStreamingApiRequest', options);
|
|
3510
|
+
};
|
|
3511
|
+
|
|
3503
3512
|
const getTextContent = content => {
|
|
3504
3513
|
if (typeof content === 'string') {
|
|
3505
3514
|
return content;
|
|
@@ -3521,6 +3530,8 @@ const getTextContent = content => {
|
|
|
3521
3530
|
return textParts.join('\n');
|
|
3522
3531
|
};
|
|
3523
3532
|
|
|
3533
|
+
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
|
|
3534
|
+
|
|
3524
3535
|
const getOpenAiTools = tools => {
|
|
3525
3536
|
return tools.map(tool => {
|
|
3526
3537
|
if (!tool || typeof tool !== 'object') {
|
|
@@ -4028,15 +4039,44 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
4028
4039
|
...(responseId ? {
|
|
4029
4040
|
responseId
|
|
4030
4041
|
} : {}),
|
|
4031
|
-
responseFunctionCalls,
|
|
4032
|
-
text,
|
|
4033
|
-
type: 'success'
|
|
4042
|
+
responseFunctionCalls,
|
|
4043
|
+
text,
|
|
4044
|
+
type: 'success'
|
|
4045
|
+
};
|
|
4046
|
+
};
|
|
4047
|
+
const getOpenApiErrorDetails = async response => {
|
|
4048
|
+
let parsed;
|
|
4049
|
+
try {
|
|
4050
|
+
parsed = await response.json();
|
|
4051
|
+
} catch {
|
|
4052
|
+
return {};
|
|
4053
|
+
}
|
|
4054
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
4055
|
+
return {};
|
|
4056
|
+
}
|
|
4057
|
+
const error = Reflect.get(parsed, 'error');
|
|
4058
|
+
if (!error || typeof error !== 'object') {
|
|
4059
|
+
return {};
|
|
4060
|
+
}
|
|
4061
|
+
const errorCode = Reflect.get(error, 'code');
|
|
4062
|
+
const errorMessage = Reflect.get(error, 'message');
|
|
4063
|
+
const errorType = Reflect.get(error, 'type');
|
|
4064
|
+
return {
|
|
4065
|
+
...(typeof errorCode === 'string' ? {
|
|
4066
|
+
errorCode
|
|
4067
|
+
} : {}),
|
|
4068
|
+
...(typeof errorMessage === 'string' ? {
|
|
4069
|
+
errorMessage
|
|
4070
|
+
} : {}),
|
|
4071
|
+
...(typeof errorType === 'string' ? {
|
|
4072
|
+
errorType
|
|
4073
|
+
} : {})
|
|
4034
4074
|
};
|
|
4035
4075
|
};
|
|
4036
|
-
const
|
|
4076
|
+
const getOpenApiErrorDetailsFromResponseText = responseText => {
|
|
4037
4077
|
let parsed;
|
|
4038
4078
|
try {
|
|
4039
|
-
parsed =
|
|
4079
|
+
parsed = JSON.parse(responseText);
|
|
4040
4080
|
} catch {
|
|
4041
4081
|
return {};
|
|
4042
4082
|
}
|
|
@@ -4062,6 +4102,23 @@ const getOpenApiErrorDetails = async response => {
|
|
|
4062
4102
|
} : {})
|
|
4063
4103
|
};
|
|
4064
4104
|
};
|
|
4105
|
+
const getResponseFromSseEvents = events => {
|
|
4106
|
+
const chunks = events.map(event => {
|
|
4107
|
+
const data = typeof event === 'string' ? event : JSON.stringify(event);
|
|
4108
|
+
return `data: ${data}\n\n`;
|
|
4109
|
+
});
|
|
4110
|
+
const stream = new ReadableStream({
|
|
4111
|
+
start(controller) {
|
|
4112
|
+
for (const chunk of chunks) {
|
|
4113
|
+
controller.enqueue(new TextEncoder().encode(chunk));
|
|
4114
|
+
}
|
|
4115
|
+
controller.close();
|
|
4116
|
+
}
|
|
4117
|
+
});
|
|
4118
|
+
return {
|
|
4119
|
+
body: stream
|
|
4120
|
+
};
|
|
4121
|
+
};
|
|
4065
4122
|
const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApiApiBaseUrl, assetDir, platform, options) => {
|
|
4066
4123
|
const {
|
|
4067
4124
|
includeObfuscation = false,
|
|
@@ -4070,6 +4127,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
4070
4127
|
onTextChunk,
|
|
4071
4128
|
onToolCallsChunk,
|
|
4072
4129
|
stream,
|
|
4130
|
+
useChatNetworkWorkerForRequests = false,
|
|
4073
4131
|
webSearchEnabled = false
|
|
4074
4132
|
} = options ?? {
|
|
4075
4133
|
stream: false
|
|
@@ -4082,46 +4140,89 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
4082
4140
|
const maxToolIterations = 4;
|
|
4083
4141
|
let previousResponseId;
|
|
4084
4142
|
for (let i = 0; i <= maxToolIterations; i++) {
|
|
4085
|
-
|
|
4086
|
-
try {
|
|
4087
|
-
response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
|
|
4088
|
-
body: JSON.stringify(getOpenAiParams(openAiInput, modelId, stream, includeObfuscation, tools, webSearchEnabled, previousResponseId)),
|
|
4089
|
-
headers: {
|
|
4090
|
-
Authorization: `Bearer ${openApiApiKey}`,
|
|
4091
|
-
'Content-Type': 'application/json',
|
|
4092
|
-
...getClientRequestIdHeader()
|
|
4093
|
-
},
|
|
4094
|
-
method: 'POST'
|
|
4095
|
-
});
|
|
4096
|
-
} catch {
|
|
4097
|
-
return {
|
|
4098
|
-
details: 'request-failed',
|
|
4099
|
-
type: 'error'
|
|
4100
|
-
};
|
|
4101
|
-
}
|
|
4102
|
-
if (!response.ok) {
|
|
4103
|
-
const {
|
|
4104
|
-
errorCode,
|
|
4105
|
-
errorMessage,
|
|
4106
|
-
errorType
|
|
4107
|
-
} = await getOpenApiErrorDetails(response);
|
|
4108
|
-
return {
|
|
4109
|
-
details: 'http-error',
|
|
4110
|
-
...(errorCode ? {
|
|
4111
|
-
errorCode
|
|
4112
|
-
} : {}),
|
|
4113
|
-
...(errorMessage ? {
|
|
4114
|
-
errorMessage
|
|
4115
|
-
} : {}),
|
|
4116
|
-
...(errorType ? {
|
|
4117
|
-
errorType
|
|
4118
|
-
} : {}),
|
|
4119
|
-
statusCode: response.status,
|
|
4120
|
-
type: 'error'
|
|
4121
|
-
};
|
|
4122
|
-
}
|
|
4143
|
+
const postBody = getOpenAiParams(openAiInput, modelId, stream, includeObfuscation, tools, webSearchEnabled, previousResponseId);
|
|
4123
4144
|
if (stream) {
|
|
4124
|
-
const streamResult = await
|
|
4145
|
+
const streamResult = useChatNetworkWorkerForRequests ? await (async () => {
|
|
4146
|
+
const requestResult = await makeStreamingApiRequest({
|
|
4147
|
+
headers: {
|
|
4148
|
+
Authorization: `Bearer ${openApiApiKey}`,
|
|
4149
|
+
'Content-Type': 'application/json',
|
|
4150
|
+
...getClientRequestIdHeader()
|
|
4151
|
+
},
|
|
4152
|
+
method: 'POST',
|
|
4153
|
+
postBody,
|
|
4154
|
+
url: getOpenApiApiEndpoint(openApiApiBaseUrl)
|
|
4155
|
+
});
|
|
4156
|
+
if (requestResult.type === 'error') {
|
|
4157
|
+
if (requestResult.statusCode === 0) {
|
|
4158
|
+
return {
|
|
4159
|
+
details: 'request-failed',
|
|
4160
|
+
type: 'error'
|
|
4161
|
+
};
|
|
4162
|
+
}
|
|
4163
|
+
const {
|
|
4164
|
+
errorCode,
|
|
4165
|
+
errorMessage,
|
|
4166
|
+
errorType
|
|
4167
|
+
} = getOpenApiErrorDetailsFromResponseText(requestResult.response);
|
|
4168
|
+
return {
|
|
4169
|
+
details: 'http-error',
|
|
4170
|
+
...(errorCode ? {
|
|
4171
|
+
errorCode
|
|
4172
|
+
} : {}),
|
|
4173
|
+
...(errorMessage ? {
|
|
4174
|
+
errorMessage
|
|
4175
|
+
} : {}),
|
|
4176
|
+
...(errorType ? {
|
|
4177
|
+
errorType
|
|
4178
|
+
} : {}),
|
|
4179
|
+
statusCode: requestResult.statusCode,
|
|
4180
|
+
type: 'error'
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
const response = getResponseFromSseEvents(requestResult.body);
|
|
4184
|
+
return parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent);
|
|
4185
|
+
})() : await (async () => {
|
|
4186
|
+
let response;
|
|
4187
|
+
try {
|
|
4188
|
+
response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
|
|
4189
|
+
body: JSON.stringify(postBody),
|
|
4190
|
+
headers: {
|
|
4191
|
+
Authorization: `Bearer ${openApiApiKey}`,
|
|
4192
|
+
'Content-Type': 'application/json',
|
|
4193
|
+
...getClientRequestIdHeader()
|
|
4194
|
+
},
|
|
4195
|
+
method: 'POST'
|
|
4196
|
+
});
|
|
4197
|
+
} catch {
|
|
4198
|
+
return {
|
|
4199
|
+
details: 'request-failed',
|
|
4200
|
+
type: 'error'
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
if (!response.ok) {
|
|
4204
|
+
const {
|
|
4205
|
+
errorCode,
|
|
4206
|
+
errorMessage,
|
|
4207
|
+
errorType
|
|
4208
|
+
} = await getOpenApiErrorDetails(response);
|
|
4209
|
+
return {
|
|
4210
|
+
details: 'http-error',
|
|
4211
|
+
...(errorCode ? {
|
|
4212
|
+
errorCode
|
|
4213
|
+
} : {}),
|
|
4214
|
+
...(errorMessage ? {
|
|
4215
|
+
errorMessage
|
|
4216
|
+
} : {}),
|
|
4217
|
+
...(errorType ? {
|
|
4218
|
+
errorType
|
|
4219
|
+
} : {}),
|
|
4220
|
+
statusCode: response.status,
|
|
4221
|
+
type: 'error'
|
|
4222
|
+
};
|
|
4223
|
+
}
|
|
4224
|
+
return parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent);
|
|
4225
|
+
})();
|
|
4125
4226
|
if (streamResult.type !== 'success') {
|
|
4126
4227
|
return streamResult;
|
|
4127
4228
|
}
|
|
@@ -4168,13 +4269,92 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
4168
4269
|
};
|
|
4169
4270
|
}
|
|
4170
4271
|
let parsed;
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4272
|
+
if (useChatNetworkWorkerForRequests) {
|
|
4273
|
+
const requestResult = await makeApiRequest({
|
|
4274
|
+
headers: {
|
|
4275
|
+
Authorization: `Bearer ${openApiApiKey}`,
|
|
4276
|
+
'Content-Type': 'application/json',
|
|
4277
|
+
...getClientRequestIdHeader()
|
|
4278
|
+
},
|
|
4279
|
+
method: 'POST',
|
|
4280
|
+
postBody,
|
|
4281
|
+
url: getOpenApiApiEndpoint(openApiApiBaseUrl)
|
|
4282
|
+
});
|
|
4283
|
+
if (requestResult.type === 'error') {
|
|
4284
|
+
if (requestResult.statusCode === 0) {
|
|
4285
|
+
return {
|
|
4286
|
+
details: 'request-failed',
|
|
4287
|
+
type: 'error'
|
|
4288
|
+
};
|
|
4289
|
+
}
|
|
4290
|
+
const {
|
|
4291
|
+
errorCode,
|
|
4292
|
+
errorMessage,
|
|
4293
|
+
errorType
|
|
4294
|
+
} = getOpenApiErrorDetailsFromResponseText(requestResult.response);
|
|
4295
|
+
return {
|
|
4296
|
+
details: 'http-error',
|
|
4297
|
+
...(errorCode ? {
|
|
4298
|
+
errorCode
|
|
4299
|
+
} : {}),
|
|
4300
|
+
...(errorMessage ? {
|
|
4301
|
+
errorMessage
|
|
4302
|
+
} : {}),
|
|
4303
|
+
...(errorType ? {
|
|
4304
|
+
errorType
|
|
4305
|
+
} : {}),
|
|
4306
|
+
statusCode: requestResult.statusCode,
|
|
4307
|
+
type: 'error'
|
|
4308
|
+
};
|
|
4309
|
+
}
|
|
4310
|
+
parsed = requestResult.body;
|
|
4311
|
+
} else {
|
|
4312
|
+
let response;
|
|
4313
|
+
try {
|
|
4314
|
+
response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
|
|
4315
|
+
body: JSON.stringify(postBody),
|
|
4316
|
+
headers: {
|
|
4317
|
+
Authorization: `Bearer ${openApiApiKey}`,
|
|
4318
|
+
'Content-Type': 'application/json',
|
|
4319
|
+
...getClientRequestIdHeader()
|
|
4320
|
+
},
|
|
4321
|
+
method: 'POST'
|
|
4322
|
+
});
|
|
4323
|
+
} catch {
|
|
4324
|
+
return {
|
|
4325
|
+
details: 'request-failed',
|
|
4326
|
+
type: 'error'
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
if (!response.ok) {
|
|
4330
|
+
const {
|
|
4331
|
+
errorCode,
|
|
4332
|
+
errorMessage,
|
|
4333
|
+
errorType
|
|
4334
|
+
} = await getOpenApiErrorDetails(response);
|
|
4335
|
+
return {
|
|
4336
|
+
details: 'http-error',
|
|
4337
|
+
...(errorCode ? {
|
|
4338
|
+
errorCode
|
|
4339
|
+
} : {}),
|
|
4340
|
+
...(errorMessage ? {
|
|
4341
|
+
errorMessage
|
|
4342
|
+
} : {}),
|
|
4343
|
+
...(errorType ? {
|
|
4344
|
+
errorType
|
|
4345
|
+
} : {}),
|
|
4346
|
+
statusCode: response.status,
|
|
4347
|
+
type: 'error'
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4350
|
+
try {
|
|
4351
|
+
parsed = await response.json();
|
|
4352
|
+
} catch {
|
|
4353
|
+
return {
|
|
4354
|
+
details: 'request-failed',
|
|
4355
|
+
type: 'error'
|
|
4356
|
+
};
|
|
4357
|
+
}
|
|
4178
4358
|
}
|
|
4179
4359
|
if (!parsed || typeof parsed !== 'object') {
|
|
4180
4360
|
return {
|
|
@@ -4409,28 +4589,67 @@ const getOpenRouterRaw429Message = async response => {
|
|
|
4409
4589
|
}
|
|
4410
4590
|
return raw;
|
|
4411
4591
|
};
|
|
4412
|
-
const
|
|
4413
|
-
let
|
|
4592
|
+
const getOpenRouterRaw429MessageFromText = responseText => {
|
|
4593
|
+
let parsed;
|
|
4414
4594
|
try {
|
|
4415
|
-
|
|
4416
|
-
headers: {
|
|
4417
|
-
Authorization: `Bearer ${openRouterApiKey}`,
|
|
4418
|
-
...getClientRequestIdHeader()
|
|
4419
|
-
},
|
|
4420
|
-
method: 'GET'
|
|
4421
|
-
});
|
|
4595
|
+
parsed = JSON.parse(responseText);
|
|
4422
4596
|
} catch {
|
|
4423
4597
|
return undefined;
|
|
4424
4598
|
}
|
|
4425
|
-
if (!
|
|
4599
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
4426
4600
|
return undefined;
|
|
4427
4601
|
}
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
}
|
|
4602
|
+
const error = Reflect.get(parsed, 'error');
|
|
4603
|
+
if (!error || typeof error !== 'object') {
|
|
4604
|
+
return undefined;
|
|
4605
|
+
}
|
|
4606
|
+
const metadata = Reflect.get(error, 'metadata');
|
|
4607
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
4608
|
+
return undefined;
|
|
4609
|
+
}
|
|
4610
|
+
const raw = Reflect.get(metadata, 'raw');
|
|
4611
|
+
if (typeof raw !== 'string' || !raw) {
|
|
4432
4612
|
return undefined;
|
|
4433
4613
|
}
|
|
4614
|
+
return raw;
|
|
4615
|
+
};
|
|
4616
|
+
const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl, useChatNetworkWorkerForRequests) => {
|
|
4617
|
+
let parsed;
|
|
4618
|
+
if (useChatNetworkWorkerForRequests) {
|
|
4619
|
+
const result = await makeApiRequest({
|
|
4620
|
+
headers: {
|
|
4621
|
+
Authorization: `Bearer ${openRouterApiKey}`,
|
|
4622
|
+
...getClientRequestIdHeader()
|
|
4623
|
+
},
|
|
4624
|
+
method: 'GET',
|
|
4625
|
+
url: getOpenRouterKeyEndpoint(openRouterApiBaseUrl)
|
|
4626
|
+
});
|
|
4627
|
+
if (result.type === 'error') {
|
|
4628
|
+
return undefined;
|
|
4629
|
+
}
|
|
4630
|
+
parsed = result.body;
|
|
4631
|
+
} else {
|
|
4632
|
+
let response;
|
|
4633
|
+
try {
|
|
4634
|
+
response = await fetch(getOpenRouterKeyEndpoint(openRouterApiBaseUrl), {
|
|
4635
|
+
headers: {
|
|
4636
|
+
Authorization: `Bearer ${openRouterApiKey}`,
|
|
4637
|
+
...getClientRequestIdHeader()
|
|
4638
|
+
},
|
|
4639
|
+
method: 'GET'
|
|
4640
|
+
});
|
|
4641
|
+
} catch {
|
|
4642
|
+
return undefined;
|
|
4643
|
+
}
|
|
4644
|
+
if (!response.ok) {
|
|
4645
|
+
return undefined;
|
|
4646
|
+
}
|
|
4647
|
+
try {
|
|
4648
|
+
parsed = await response.json();
|
|
4649
|
+
} catch {
|
|
4650
|
+
return undefined;
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4434
4653
|
if (!parsed || typeof parsed !== 'object') {
|
|
4435
4654
|
return undefined;
|
|
4436
4655
|
}
|
|
@@ -4462,7 +4681,7 @@ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl) =>
|
|
|
4462
4681
|
}
|
|
4463
4682
|
return normalizedLimitInfo;
|
|
4464
4683
|
};
|
|
4465
|
-
const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform) => {
|
|
4684
|
+
const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests = false) => {
|
|
4466
4685
|
const completionMessages = messages.map(message => ({
|
|
4467
4686
|
content: message.text,
|
|
4468
4687
|
role: message.role
|
|
@@ -4470,64 +4689,111 @@ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, o
|
|
|
4470
4689
|
const tools = getBasicChatTools();
|
|
4471
4690
|
const maxToolIterations = 4;
|
|
4472
4691
|
for (let i = 0; i <= maxToolIterations; i++) {
|
|
4473
|
-
let
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
body: JSON.stringify({
|
|
4477
|
-
messages: completionMessages,
|
|
4478
|
-
model: modelId,
|
|
4479
|
-
tool_choice: 'auto',
|
|
4480
|
-
tools
|
|
4481
|
-
}),
|
|
4692
|
+
let parsed;
|
|
4693
|
+
if (useChatNetworkWorkerForRequests) {
|
|
4694
|
+
const requestResult = await makeApiRequest({
|
|
4482
4695
|
headers: {
|
|
4483
4696
|
Authorization: `Bearer ${openRouterApiKey}`,
|
|
4484
4697
|
'Content-Type': 'application/json',
|
|
4485
4698
|
...getClientRequestIdHeader()
|
|
4486
4699
|
},
|
|
4487
|
-
method: 'POST'
|
|
4700
|
+
method: 'POST',
|
|
4701
|
+
postBody: {
|
|
4702
|
+
messages: completionMessages,
|
|
4703
|
+
model: modelId,
|
|
4704
|
+
tool_choice: 'auto',
|
|
4705
|
+
tools
|
|
4706
|
+
},
|
|
4707
|
+
url: getOpenRouterApiEndpoint(openRouterApiBaseUrl)
|
|
4488
4708
|
});
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4709
|
+
if (requestResult.type === 'error') {
|
|
4710
|
+
if (requestResult.statusCode === 429) {
|
|
4711
|
+
const retryAfter = requestResult.headers?.['retry-after'] ?? null;
|
|
4712
|
+
const rawMessage = getOpenRouterRaw429MessageFromText(requestResult.response);
|
|
4713
|
+
const limitInfo = await getOpenRouterLimitInfo(openRouterApiKey, openRouterApiBaseUrl, useChatNetworkWorkerForRequests);
|
|
4714
|
+
return {
|
|
4715
|
+
details: 'too-many-requests',
|
|
4716
|
+
...(limitInfo || retryAfter ? {
|
|
4717
|
+
limitInfo: {
|
|
4718
|
+
...limitInfo,
|
|
4719
|
+
...(retryAfter ? {
|
|
4720
|
+
retryAfter
|
|
4721
|
+
} : {})
|
|
4722
|
+
}
|
|
4723
|
+
} : {}),
|
|
4724
|
+
...(rawMessage ? {
|
|
4725
|
+
rawMessage
|
|
4726
|
+
} : {}),
|
|
4727
|
+
statusCode: 429,
|
|
4728
|
+
type: 'error'
|
|
4729
|
+
};
|
|
4730
|
+
}
|
|
4500
4731
|
return {
|
|
4501
|
-
details: '
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4732
|
+
details: 'http-error',
|
|
4733
|
+
statusCode: requestResult.statusCode,
|
|
4734
|
+
type: 'error'
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
parsed = requestResult.body;
|
|
4738
|
+
} else {
|
|
4739
|
+
let response;
|
|
4740
|
+
try {
|
|
4741
|
+
response = await fetch(getOpenRouterApiEndpoint(openRouterApiBaseUrl), {
|
|
4742
|
+
body: JSON.stringify({
|
|
4743
|
+
messages: completionMessages,
|
|
4744
|
+
model: modelId,
|
|
4745
|
+
tool_choice: 'auto',
|
|
4746
|
+
tools
|
|
4747
|
+
}),
|
|
4748
|
+
headers: {
|
|
4749
|
+
Authorization: `Bearer ${openRouterApiKey}`,
|
|
4750
|
+
'Content-Type': 'application/json',
|
|
4751
|
+
...getClientRequestIdHeader()
|
|
4752
|
+
},
|
|
4753
|
+
method: 'POST'
|
|
4754
|
+
});
|
|
4755
|
+
} catch {
|
|
4756
|
+
return {
|
|
4757
|
+
details: 'request-failed',
|
|
4758
|
+
type: 'error'
|
|
4759
|
+
};
|
|
4760
|
+
}
|
|
4761
|
+
if (!response.ok) {
|
|
4762
|
+
if (response.status === 429) {
|
|
4763
|
+
const retryAfter = response.headers?.get?.('retry-after') ?? null;
|
|
4764
|
+
const rawMessage = await getOpenRouterRaw429Message(response);
|
|
4765
|
+
const limitInfo = await getOpenRouterLimitInfo(openRouterApiKey, openRouterApiBaseUrl, useChatNetworkWorkerForRequests);
|
|
4766
|
+
return {
|
|
4767
|
+
details: 'too-many-requests',
|
|
4768
|
+
...(limitInfo || retryAfter ? {
|
|
4769
|
+
limitInfo: {
|
|
4770
|
+
...limitInfo,
|
|
4771
|
+
...(retryAfter ? {
|
|
4772
|
+
retryAfter
|
|
4773
|
+
} : {})
|
|
4774
|
+
}
|
|
4775
|
+
} : {}),
|
|
4776
|
+
...(rawMessage ? {
|
|
4777
|
+
rawMessage
|
|
4778
|
+
} : {}),
|
|
4779
|
+
statusCode: 429,
|
|
4780
|
+
type: 'error'
|
|
4781
|
+
};
|
|
4782
|
+
}
|
|
4783
|
+
return {
|
|
4784
|
+
details: 'http-error',
|
|
4785
|
+
statusCode: response.status,
|
|
4786
|
+
type: 'error'
|
|
4787
|
+
};
|
|
4788
|
+
}
|
|
4789
|
+
try {
|
|
4790
|
+
parsed = await response.json();
|
|
4791
|
+
} catch {
|
|
4792
|
+
return {
|
|
4793
|
+
details: 'request-failed',
|
|
4514
4794
|
type: 'error'
|
|
4515
4795
|
};
|
|
4516
4796
|
}
|
|
4517
|
-
return {
|
|
4518
|
-
details: 'http-error',
|
|
4519
|
-
statusCode: response.status,
|
|
4520
|
-
type: 'error'
|
|
4521
|
-
};
|
|
4522
|
-
}
|
|
4523
|
-
let parsed;
|
|
4524
|
-
try {
|
|
4525
|
-
parsed = await response.json();
|
|
4526
|
-
} catch {
|
|
4527
|
-
return {
|
|
4528
|
-
details: 'request-failed',
|
|
4529
|
-
type: 'error'
|
|
4530
|
-
};
|
|
4531
4797
|
}
|
|
4532
4798
|
if (!parsed || typeof parsed !== 'object') {
|
|
4533
4799
|
return {
|
|
@@ -4667,6 +4933,17 @@ const isOpenRouterModel = (selectedModelId, models) => {
|
|
|
4667
4933
|
return selectedModelId.toLowerCase().startsWith('openrouter/');
|
|
4668
4934
|
};
|
|
4669
4935
|
|
|
4936
|
+
let requests = [];
|
|
4937
|
+
const reset$1 = () => {
|
|
4938
|
+
requests = [];
|
|
4939
|
+
};
|
|
4940
|
+
const capture = request => {
|
|
4941
|
+
requests = [...requests, request];
|
|
4942
|
+
};
|
|
4943
|
+
const getAll = () => {
|
|
4944
|
+
return requests;
|
|
4945
|
+
};
|
|
4946
|
+
|
|
4670
4947
|
const getAiResponse = async ({
|
|
4671
4948
|
assetDir,
|
|
4672
4949
|
messageId,
|
|
@@ -4686,6 +4963,7 @@ const getAiResponse = async ({
|
|
|
4686
4963
|
platform,
|
|
4687
4964
|
selectedModelId,
|
|
4688
4965
|
streamingEnabled = true,
|
|
4966
|
+
useChatNetworkWorkerForRequests = false,
|
|
4689
4967
|
useMockApi,
|
|
4690
4968
|
userText,
|
|
4691
4969
|
webSearchEnabled = false
|
|
@@ -4695,6 +4973,22 @@ const getAiResponse = async ({
|
|
|
4695
4973
|
const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
|
|
4696
4974
|
if (usesOpenApiModel) {
|
|
4697
4975
|
if (useMockApi) {
|
|
4976
|
+
const openAiInput = messages.map(message => ({
|
|
4977
|
+
content: message.text,
|
|
4978
|
+
role: message.role
|
|
4979
|
+
}));
|
|
4980
|
+
const modelId = getOpenApiModelId(selectedModelId);
|
|
4981
|
+
const headers = {
|
|
4982
|
+
Authorization: `Bearer ${openApiApiKey}`,
|
|
4983
|
+
'Content-Type': 'application/json',
|
|
4984
|
+
...getClientRequestIdHeader()
|
|
4985
|
+
};
|
|
4986
|
+
capture({
|
|
4987
|
+
headers,
|
|
4988
|
+
method: 'POST',
|
|
4989
|
+
payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, getBasicChatTools(), webSearchEnabled),
|
|
4990
|
+
url: getOpenApiApiEndpoint(openApiApiBaseUrl)
|
|
4991
|
+
});
|
|
4698
4992
|
const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
|
|
4699
4993
|
if (result.type === 'success') {
|
|
4700
4994
|
const {
|
|
@@ -4720,6 +5014,7 @@ const getAiResponse = async ({
|
|
|
4720
5014
|
onToolCallsChunk
|
|
4721
5015
|
} : {}),
|
|
4722
5016
|
stream: streamingEnabled,
|
|
5017
|
+
useChatNetworkWorkerForRequests,
|
|
4723
5018
|
webSearchEnabled
|
|
4724
5019
|
});
|
|
4725
5020
|
if (result.type === 'success') {
|
|
@@ -4746,7 +5041,7 @@ const getAiResponse = async ({
|
|
|
4746
5041
|
text = getOpenRouterErrorMessage(result);
|
|
4747
5042
|
}
|
|
4748
5043
|
} else if (openRouterApiKey) {
|
|
4749
|
-
const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform);
|
|
5044
|
+
const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests);
|
|
4750
5045
|
if (result.type === 'success') {
|
|
4751
5046
|
const {
|
|
4752
5047
|
text: assistantText
|
|
@@ -4830,6 +5125,7 @@ const handleClickSaveOpenApiApiKey = async state => {
|
|
|
4830
5125
|
platform: updatedState.platform,
|
|
4831
5126
|
selectedModelId: updatedState.selectedModelId,
|
|
4832
5127
|
streamingEnabled: updatedState.streamingEnabled,
|
|
5128
|
+
useChatNetworkWorkerForRequests: updatedState.useChatNetworkWorkerForRequests,
|
|
4833
5129
|
useMockApi: updatedState.useMockApi,
|
|
4834
5130
|
userText: previousUserMessage.text
|
|
4835
5131
|
});
|
|
@@ -4910,6 +5206,7 @@ const handleClickSaveOpenRouterApiKey = async state => {
|
|
|
4910
5206
|
openRouterApiKey,
|
|
4911
5207
|
platform: updatedState.platform,
|
|
4912
5208
|
selectedModelId: updatedState.selectedModelId,
|
|
5209
|
+
useChatNetworkWorkerForRequests: updatedState.useChatNetworkWorkerForRequests,
|
|
4913
5210
|
useMockApi: updatedState.useMockApi,
|
|
4914
5211
|
userText: previousUserMessage.text
|
|
4915
5212
|
});
|
|
@@ -5286,6 +5583,7 @@ Assistant: ${assistantText}`;
|
|
|
5286
5583
|
platform: state.platform,
|
|
5287
5584
|
selectedModelId,
|
|
5288
5585
|
streamingEnabled: false,
|
|
5586
|
+
useChatNetworkWorkerForRequests: state.useChatNetworkWorkerForRequests,
|
|
5289
5587
|
useMockApi,
|
|
5290
5588
|
userText: titlePrompt,
|
|
5291
5589
|
webSearchEnabled: false
|
|
@@ -5313,6 +5611,7 @@ const handleSubmit = async state => {
|
|
|
5313
5611
|
selectedSessionId,
|
|
5314
5612
|
sessions,
|
|
5315
5613
|
streamingEnabled,
|
|
5614
|
+
useChatNetworkWorkerForRequests,
|
|
5316
5615
|
useMockApi,
|
|
5317
5616
|
viewMode,
|
|
5318
5617
|
webSearchEnabled
|
|
@@ -5465,6 +5764,7 @@ const handleSubmit = async state => {
|
|
|
5465
5764
|
platform,
|
|
5466
5765
|
selectedModelId,
|
|
5467
5766
|
streamingEnabled,
|
|
5767
|
+
useChatNetworkWorkerForRequests,
|
|
5468
5768
|
useMockApi,
|
|
5469
5769
|
userText,
|
|
5470
5770
|
webSearchEnabled
|
|
@@ -6105,8 +6405,17 @@ const loadStreamingEnabled = async () => {
|
|
|
6105
6405
|
}
|
|
6106
6406
|
};
|
|
6107
6407
|
|
|
6408
|
+
const loadUseChatNetworkWorkerForRequests = async () => {
|
|
6409
|
+
try {
|
|
6410
|
+
const savedUseChatNetworkWorkerForRequests = await get('chatView.useChatNetworkWorkerForRequests');
|
|
6411
|
+
return typeof savedUseChatNetworkWorkerForRequests === 'boolean' ? savedUseChatNetworkWorkerForRequests : false;
|
|
6412
|
+
} catch {
|
|
6413
|
+
return false;
|
|
6414
|
+
}
|
|
6415
|
+
};
|
|
6416
|
+
|
|
6108
6417
|
const loadPreferences = async () => {
|
|
6109
|
-
const [aiSessionTitleGenerationEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation()]);
|
|
6418
|
+
const [aiSessionTitleGenerationEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation, useChatNetworkWorkerForRequests] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation(), loadUseChatNetworkWorkerForRequests()]);
|
|
6110
6419
|
return {
|
|
6111
6420
|
aiSessionTitleGenerationEnabled,
|
|
6112
6421
|
composerDropEnabled,
|
|
@@ -6114,7 +6423,8 @@ const loadPreferences = async () => {
|
|
|
6114
6423
|
openApiApiKey,
|
|
6115
6424
|
openRouterApiKey,
|
|
6116
6425
|
passIncludeObfuscation,
|
|
6117
|
-
streamingEnabled
|
|
6426
|
+
streamingEnabled,
|
|
6427
|
+
useChatNetworkWorkerForRequests
|
|
6118
6428
|
};
|
|
6119
6429
|
};
|
|
6120
6430
|
|
|
@@ -6151,7 +6461,8 @@ const loadContent = async (state, savedState) => {
|
|
|
6151
6461
|
openApiApiKey,
|
|
6152
6462
|
openRouterApiKey,
|
|
6153
6463
|
passIncludeObfuscation,
|
|
6154
|
-
streamingEnabled
|
|
6464
|
+
streamingEnabled,
|
|
6465
|
+
useChatNetworkWorkerForRequests
|
|
6155
6466
|
} = await loadPreferences();
|
|
6156
6467
|
const legacySavedSessions = getSavedSessions(savedState);
|
|
6157
6468
|
const storedSessions = await listChatSessions();
|
|
@@ -6195,10 +6506,20 @@ const loadContent = async (state, savedState) => {
|
|
|
6195
6506
|
selectedSessionId,
|
|
6196
6507
|
sessions,
|
|
6197
6508
|
streamingEnabled,
|
|
6509
|
+
useChatNetworkWorkerForRequests,
|
|
6198
6510
|
viewMode
|
|
6199
6511
|
};
|
|
6200
6512
|
};
|
|
6201
6513
|
|
|
6514
|
+
const mockOpenApiRequestGetAll = _state => {
|
|
6515
|
+
return getAll();
|
|
6516
|
+
};
|
|
6517
|
+
|
|
6518
|
+
const mockOpenApiRequestReset = state => {
|
|
6519
|
+
reset$1();
|
|
6520
|
+
return state;
|
|
6521
|
+
};
|
|
6522
|
+
|
|
6202
6523
|
const mockOpenApiSetHttpErrorResponse = (state, statusCode, body) => {
|
|
6203
6524
|
setHttpErrorResponse(statusCode, body);
|
|
6204
6525
|
return state;
|
|
@@ -6215,7 +6536,7 @@ const mockOpenApiStreamPushChunk = (state, chunk) => {
|
|
|
6215
6536
|
};
|
|
6216
6537
|
|
|
6217
6538
|
const mockOpenApiStreamReset = state => {
|
|
6218
|
-
reset$
|
|
6539
|
+
reset$2();
|
|
6219
6540
|
return state;
|
|
6220
6541
|
};
|
|
6221
6542
|
|
|
@@ -6254,7 +6575,7 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
|
|
|
6254
6575
|
};
|
|
6255
6576
|
|
|
6256
6577
|
const registerMockResponse = (state, mockResponse) => {
|
|
6257
|
-
reset$
|
|
6578
|
+
reset$2();
|
|
6258
6579
|
pushChunk(mockResponse.text);
|
|
6259
6580
|
finish();
|
|
6260
6581
|
return state;
|
|
@@ -7936,6 +8257,18 @@ const setStreamingEnabled = (state, streamingEnabled) => {
|
|
|
7936
8257
|
};
|
|
7937
8258
|
};
|
|
7938
8259
|
|
|
8260
|
+
const setUseChatNetworkWorkerForRequests = async (state, useChatNetworkWorkerForRequests, persist = true) => {
|
|
8261
|
+
if (persist) {
|
|
8262
|
+
await update({
|
|
8263
|
+
'chatView.useChatNetworkWorkerForRequests': useChatNetworkWorkerForRequests
|
|
8264
|
+
});
|
|
8265
|
+
}
|
|
8266
|
+
return {
|
|
8267
|
+
...state,
|
|
8268
|
+
useChatNetworkWorkerForRequests
|
|
8269
|
+
};
|
|
8270
|
+
};
|
|
8271
|
+
|
|
7939
8272
|
const defaultMockApiCommandId = 'ChatE2e.mockApi';
|
|
7940
8273
|
const useMockApi = (state, value, mockApiCommandId = defaultMockApiCommandId) => {
|
|
7941
8274
|
if (!value) {
|
|
@@ -7985,6 +8318,8 @@ const commandMap = {
|
|
|
7985
8318
|
'Chat.initialize': initialize,
|
|
7986
8319
|
'Chat.loadContent': wrapCommand(loadContent),
|
|
7987
8320
|
'Chat.loadContent2': wrapCommand(loadContent),
|
|
8321
|
+
'Chat.mockOpenApiRequestGetAll': wrapGetter(mockOpenApiRequestGetAll),
|
|
8322
|
+
'Chat.mockOpenApiRequestReset': wrapCommand(mockOpenApiRequestReset),
|
|
7988
8323
|
'Chat.mockOpenApiSetHttpErrorResponse': wrapCommand(mockOpenApiSetHttpErrorResponse),
|
|
7989
8324
|
'Chat.mockOpenApiStreamFinish': wrapCommand(mockOpenApiStreamFinish),
|
|
7990
8325
|
'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
|
|
@@ -8001,6 +8336,7 @@ const commandMap = {
|
|
|
8001
8336
|
'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
|
|
8002
8337
|
'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
|
|
8003
8338
|
'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
|
|
8339
|
+
'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),
|
|
8004
8340
|
'Chat.terminate': terminate,
|
|
8005
8341
|
'Chat.useMockApi': wrapCommand(useMockApi)
|
|
8006
8342
|
};
|