dsh-grok-subscription 1.1.3 → 2.0.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/CHANGELOG.md +23 -0
- package/README.en.md +11 -26
- package/README.md +12 -29
- package/docs/guide.en.md +17 -50
- package/docs/guide.zh-CN.md +18 -51
- package/lib/client.js +15 -7
- package/lib/index.js +46 -624
- package/package.json +2 -1
package/lib/index.js
CHANGED
|
@@ -7,7 +7,6 @@ var DISPLAY_NAME_ZH = "Grok \u8BA2\u9605";
|
|
|
7
7
|
var SETTINGS_NAMESPACE = "grokSubscription";
|
|
8
8
|
var CREDENTIAL_REF_NAME = "GROK_BUILD_ACCESS_TOKEN";
|
|
9
9
|
var PROXY_BASE_URL = "https://cli-chat-proxy.grok.com/v1";
|
|
10
|
-
var RESPONSES_URL = "https://cli-chat-proxy.grok.com/v1/responses";
|
|
11
10
|
var MODELS_V2_URL = "https://cli-chat-proxy.grok.com/v1/models-v2";
|
|
12
11
|
var BILLING_CREDITS_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
|
|
13
12
|
var TOKEN_AUTH_HEADER = "X-XAI-Token-Auth";
|
|
@@ -418,31 +417,6 @@ function toPiModels(models) {
|
|
|
418
417
|
};
|
|
419
418
|
});
|
|
420
419
|
}
|
|
421
|
-
function reasoningInfoOf(model) {
|
|
422
|
-
if (model?.reasoning === false) return void 0;
|
|
423
|
-
const efforts = Array.isArray(model?.reasoningEfforts) && model.reasoningEfforts.length ? model.reasoningEfforts : ["low", "medium", "high", "xhigh"];
|
|
424
|
-
const title = (id) => `${id.charAt(0).toUpperCase()}${id.slice(1)}`;
|
|
425
|
-
const preferred = model?.defaultEffort && efforts.includes(model.defaultEffort) ? model.defaultEffort : efforts.includes("high") ? "high" : efforts[efforts.length - 1];
|
|
426
|
-
return {
|
|
427
|
-
efforts: efforts.map((id) => ({ id, name: title(id) })),
|
|
428
|
-
defaultEffort: preferred
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
function toLlmModels(models) {
|
|
432
|
-
return models.map((model) => {
|
|
433
|
-
const info = {
|
|
434
|
-
provider: PROVIDER_ID,
|
|
435
|
-
id: model.id,
|
|
436
|
-
name: model.name,
|
|
437
|
-
// The bundled adapter inlines images through the host attachment store,
|
|
438
|
-
// so it advertises the same modality map as the official pi-ai path.
|
|
439
|
-
inputModalities: supportsImageInput(model.id) ? ["text", "image"] : ["text"]
|
|
440
|
-
};
|
|
441
|
-
const reasoning = reasoningInfoOf(model);
|
|
442
|
-
if (reasoning) info.reasoning = reasoning;
|
|
443
|
-
return info;
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
420
|
async function fetchLiveCatalog(accessToken, options = {}) {
|
|
447
421
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
448
422
|
const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? CATALOG_TIMEOUT_MS);
|
|
@@ -909,81 +883,7 @@ async function optionalImport(specifier, options = {}) {
|
|
|
909
883
|
}
|
|
910
884
|
return void 0;
|
|
911
885
|
}
|
|
912
|
-
function textOf(content) {
|
|
913
|
-
if (typeof content === "string") return content;
|
|
914
|
-
if (!Array.isArray(content)) return "";
|
|
915
|
-
return content.map((block) => {
|
|
916
|
-
if (typeof block === "string") return block;
|
|
917
|
-
if (block?.type === "text") return block.text ?? "";
|
|
918
|
-
if (block?.type === "reasoning") return "";
|
|
919
|
-
if (block?.type === "tool-result") {
|
|
920
|
-
return typeof block.content === "string" ? block.content : textOf(block.content);
|
|
921
|
-
}
|
|
922
|
-
return "";
|
|
923
|
-
}).join("");
|
|
924
|
-
}
|
|
925
|
-
function imagePlaceholderText(ref) {
|
|
926
|
-
const name2 = typeof ref?.name === "string" && ref.name.trim() ? ` ${JSON.stringify(ref.name.trim())}` : "";
|
|
927
|
-
return `[attached image${name2} could not be included in this request]`;
|
|
928
|
-
}
|
|
929
|
-
async function collectImageParts(options, resolveAttachments, policy = {}) {
|
|
930
|
-
const parts = /* @__PURE__ */ new Map();
|
|
931
|
-
const refs = [];
|
|
932
|
-
for (const message of options.messages ?? []) {
|
|
933
|
-
for (const block of message?.content ?? []) {
|
|
934
|
-
if (block?.type !== "image" || !block.attachment) continue;
|
|
935
|
-
const id = block.attachment.attachmentId;
|
|
936
|
-
if (typeof id !== "string" || !id || parts.has(id)) continue;
|
|
937
|
-
parts.set(id, void 0);
|
|
938
|
-
refs.push(block.attachment);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
if (refs.length === 0) return parts;
|
|
942
|
-
let store;
|
|
943
|
-
try {
|
|
944
|
-
store = resolveAttachments?.();
|
|
945
|
-
} catch {
|
|
946
|
-
store = void 0;
|
|
947
|
-
}
|
|
948
|
-
if (!store?.readImageRequest) return parts;
|
|
949
|
-
const request = {
|
|
950
|
-
maxPixels: policy.maxPixels ?? REQUEST_IMAGE_PIXEL_BUDGET,
|
|
951
|
-
maxBytes: policy.maxBytes ?? REQUEST_IMAGE_MAX_BYTES
|
|
952
|
-
};
|
|
953
|
-
await Promise.all(refs.map(async (ref) => {
|
|
954
|
-
try {
|
|
955
|
-
const version = await store.readImageRequest(ref, request);
|
|
956
|
-
if (!version?.data || typeof version.mediaType !== "string" || !version.mediaType) return;
|
|
957
|
-
parts.set(ref.attachmentId, {
|
|
958
|
-
mediaType: version.mediaType,
|
|
959
|
-
base64: Buffer.from(version.data).toString("base64")
|
|
960
|
-
});
|
|
961
|
-
} catch {
|
|
962
|
-
}
|
|
963
|
-
}));
|
|
964
|
-
return parts;
|
|
965
|
-
}
|
|
966
|
-
function imageContentParts(blocks, imageParts) {
|
|
967
|
-
const parts = [];
|
|
968
|
-
const text = textOf(blocks);
|
|
969
|
-
if (text) parts.push({ type: "input_text", text });
|
|
970
|
-
for (const block of blocks ?? []) {
|
|
971
|
-
if (block?.type !== "image" || !block.attachment) continue;
|
|
972
|
-
const resolved = imageParts?.get(block.attachment.attachmentId);
|
|
973
|
-
if (resolved) {
|
|
974
|
-
parts.push({
|
|
975
|
-
type: "input_image",
|
|
976
|
-
image_url: `data:${resolved.mediaType};base64,${resolved.base64}`
|
|
977
|
-
});
|
|
978
|
-
} else {
|
|
979
|
-
parts.push({ type: "input_text", text: imagePlaceholderText(block.attachment) });
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
return parts;
|
|
983
|
-
}
|
|
984
886
|
var PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
985
|
-
var REPLAY_KIND = "grok-build-responses";
|
|
986
|
-
var REPLAY_VERSION = 1;
|
|
987
887
|
function promptCacheKey(options) {
|
|
988
888
|
const sessionId = typeof options?.sessionId === "string" ? options.sessionId.trim() : "";
|
|
989
889
|
if (!sessionId) return void 0;
|
|
@@ -991,455 +891,10 @@ function promptCacheKey(options) {
|
|
|
991
891
|
const key = `grok${suffix}:${sessionId}`;
|
|
992
892
|
return key.length <= PROMPT_CACHE_KEY_MAX_LENGTH ? key : key.slice(0, PROMPT_CACHE_KEY_MAX_LENGTH);
|
|
993
893
|
}
|
|
994
|
-
function cacheStableTools(tools) {
|
|
995
|
-
if (!Array.isArray(tools) || tools.length === 0) return [];
|
|
996
|
-
return tools.map((tool, index) => ({ tool, index })).sort((left, right) => {
|
|
997
|
-
const byName = String(left.tool?.name ?? "").localeCompare(String(right.tool?.name ?? ""));
|
|
998
|
-
return byName === 0 ? left.index - right.index : byName;
|
|
999
|
-
}).map(({ tool }) => ({
|
|
1000
|
-
type: "function",
|
|
1001
|
-
name: tool.name,
|
|
1002
|
-
description: tool.description,
|
|
1003
|
-
parameters: tool.parameters
|
|
1004
|
-
}));
|
|
1005
|
-
}
|
|
1006
|
-
function replayEnvelope(message) {
|
|
1007
|
-
const state = message?.source?.replayState;
|
|
1008
|
-
return state?.response?.kind === REPLAY_KIND && state.response.version === REPLAY_VERSION && Array.isArray(state.blocks) ? state : void 0;
|
|
1009
|
-
}
|
|
1010
|
-
function replayReasoningItem(message, contentIndex) {
|
|
1011
|
-
const entry = replayEnvelope(message)?.blocks[contentIndex];
|
|
1012
|
-
const item = entry?.type === "reasoning" ? entry.item : void 0;
|
|
1013
|
-
return item?.type === "reasoning" && typeof item.encrypted_content === "string" && item.encrypted_content ? item : void 0;
|
|
1014
|
-
}
|
|
1015
|
-
function responsesInput(options, imageParts) {
|
|
1016
|
-
const input = [];
|
|
1017
|
-
const system = typeof options.system === "string" && options.system ? options.system : void 0;
|
|
1018
|
-
if (system) input.push({ role: "system", content: system });
|
|
1019
|
-
for (const message of options.messages ?? []) {
|
|
1020
|
-
const role = message.role === "assistant" ? "assistant" : message.role === "system" ? "system" : "user";
|
|
1021
|
-
const toolCalls = (message.content ?? []).filter((block) => block?.type === "tool-call");
|
|
1022
|
-
const toolResults = (message.content ?? []).filter((block) => block?.type === "tool-result");
|
|
1023
|
-
if (toolResults.length) {
|
|
1024
|
-
for (const block of toolResults) {
|
|
1025
|
-
input.push({
|
|
1026
|
-
type: "function_call_output",
|
|
1027
|
-
call_id: block.toolCallId ?? block.id,
|
|
1028
|
-
output: textOf(block.content)
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
if (role === "assistant" && toolCalls.length) {
|
|
1034
|
-
const content = message.content ?? [];
|
|
1035
|
-
for (let index = 0; index < content.length; index++) {
|
|
1036
|
-
const block = content[index];
|
|
1037
|
-
if (block?.type === "reasoning") {
|
|
1038
|
-
const item = replayReasoningItem(message, index);
|
|
1039
|
-
if (item) input.push(item);
|
|
1040
|
-
continue;
|
|
1041
|
-
}
|
|
1042
|
-
if (block?.type !== "tool-call") continue;
|
|
1043
|
-
input.push({
|
|
1044
|
-
type: "function_call",
|
|
1045
|
-
call_id: block.id,
|
|
1046
|
-
name: block.name,
|
|
1047
|
-
arguments: typeof block.arguments === "string" ? block.arguments : JSON.stringify(block.arguments ?? {})
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
const text2 = textOf(message.content);
|
|
1051
|
-
if (text2) input.push({ role: "assistant", content: text2 });
|
|
1052
|
-
continue;
|
|
1053
|
-
}
|
|
1054
|
-
const hasImage = (message.content ?? []).some((block) => block?.type === "image");
|
|
1055
|
-
if (role === "user" && hasImage) {
|
|
1056
|
-
const parts = imageContentParts(message.content, imageParts);
|
|
1057
|
-
if (parts.length > 0) input.push({ role, content: parts });
|
|
1058
|
-
continue;
|
|
1059
|
-
}
|
|
1060
|
-
if (role === "assistant") {
|
|
1061
|
-
const content = message.content ?? [];
|
|
1062
|
-
const output = [];
|
|
1063
|
-
let text2 = "";
|
|
1064
|
-
for (let index = 0; index < content.length; index++) {
|
|
1065
|
-
const block = content[index];
|
|
1066
|
-
if (block?.type === "reasoning") {
|
|
1067
|
-
const item = replayReasoningItem(message, index);
|
|
1068
|
-
if (item) output.push(item);
|
|
1069
|
-
} else if (block?.type === "text" && block.text) {
|
|
1070
|
-
text2 += block.text;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
if (text2) output.push({ role: "assistant", content: text2 });
|
|
1074
|
-
input.push(...output);
|
|
1075
|
-
continue;
|
|
1076
|
-
}
|
|
1077
|
-
const text = textOf(message.content);
|
|
1078
|
-
if (!text) continue;
|
|
1079
|
-
input.push({ role, content: text });
|
|
1080
|
-
}
|
|
1081
|
-
return input;
|
|
1082
|
-
}
|
|
1083
|
-
function mapFinish(reason) {
|
|
1084
|
-
if (reason === "toolUse" || reason === "tool_calls") return { kind: "tool-calls" };
|
|
1085
|
-
if (reason === "length" || reason === "max_tokens") return { kind: "max-tokens" };
|
|
1086
|
-
return { kind: "stop" };
|
|
1087
|
-
}
|
|
1088
|
-
function finiteOr(value, fallback) {
|
|
1089
|
-
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
1090
|
-
}
|
|
1091
|
-
function jsonSafeChunk(value) {
|
|
1092
|
-
if (value === null) return null;
|
|
1093
|
-
const type = typeof value;
|
|
1094
|
-
if (type === "string" || type === "boolean") return value;
|
|
1095
|
-
if (type === "number") return Number.isFinite(value) && !Object.is(value, -0) ? value : void 0;
|
|
1096
|
-
if (type !== "object") return void 0;
|
|
1097
|
-
if (Array.isArray(value)) {
|
|
1098
|
-
const list = [];
|
|
1099
|
-
for (const item of value) {
|
|
1100
|
-
const safe = jsonSafeChunk(item);
|
|
1101
|
-
if (safe !== void 0) list.push(safe);
|
|
1102
|
-
}
|
|
1103
|
-
return list;
|
|
1104
|
-
}
|
|
1105
|
-
const out = {};
|
|
1106
|
-
for (const [key, item] of Object.entries(value)) {
|
|
1107
|
-
const safe = jsonSafeChunk(item);
|
|
1108
|
-
if (safe !== void 0) out[key] = safe;
|
|
1109
|
-
}
|
|
1110
|
-
return out;
|
|
1111
|
-
}
|
|
1112
|
-
async function* streamResponsesUnsafe(options, token) {
|
|
1113
|
-
const headers = {
|
|
1114
|
-
Accept: "text/event-stream",
|
|
1115
|
-
"Content-Type": "application/json",
|
|
1116
|
-
...buildProxyHeaders(token)
|
|
1117
|
-
};
|
|
1118
|
-
try {
|
|
1119
|
-
const llm = await optionalImport("@deepseek-ai/dsh-llm");
|
|
1120
|
-
if (typeof llm?.attributionHeaders === "function") Object.assign(headers, llm.attributionHeaders());
|
|
1121
|
-
} catch {
|
|
1122
|
-
}
|
|
1123
|
-
const body = {
|
|
1124
|
-
model: options.model,
|
|
1125
|
-
input: responsesInput(options, await collectImageParts(options, options.resolveAttachments)),
|
|
1126
|
-
stream: true,
|
|
1127
|
-
// grok-4.7 returns ciphertext by default, but naming it keeps replay explicit
|
|
1128
|
-
// for every reasoning model on this proxy.
|
|
1129
|
-
include: ["reasoning.encrypted_content"]
|
|
1130
|
-
};
|
|
1131
|
-
const cacheKey = promptCacheKey(options);
|
|
1132
|
-
if (cacheKey) body.prompt_cache_key = cacheKey;
|
|
1133
|
-
if (typeof options.maxTokens === "number") body.max_output_tokens = options.maxTokens;
|
|
1134
|
-
if (typeof options.temperature === "number") body.temperature = options.temperature;
|
|
1135
|
-
const tools = cacheStableTools(options.tools);
|
|
1136
|
-
if (tools.length) body.tools = tools;
|
|
1137
|
-
if (options.reasoningEffort) body.reasoning = { effort: options.reasoningEffort };
|
|
1138
|
-
const response = await fetch(RESPONSES_URL, {
|
|
1139
|
-
method: "POST",
|
|
1140
|
-
headers,
|
|
1141
|
-
body: JSON.stringify(body),
|
|
1142
|
-
redirect: "error",
|
|
1143
|
-
signal: options.signal
|
|
1144
|
-
});
|
|
1145
|
-
if (!response.ok) {
|
|
1146
|
-
const error = new Error(`Grok Build proxy returned HTTP ${response.status}`);
|
|
1147
|
-
error.status = response.status;
|
|
1148
|
-
throw error;
|
|
1149
|
-
}
|
|
1150
|
-
if (!response.body) throw new Error("Grok Build proxy returned an empty body");
|
|
1151
|
-
const decoder = new TextDecoder();
|
|
1152
|
-
let buffer = "";
|
|
1153
|
-
let textIndex;
|
|
1154
|
-
let reasoningIndex;
|
|
1155
|
-
let textContent = "";
|
|
1156
|
-
let reasoningContent = "";
|
|
1157
|
-
let nextIndex = 0;
|
|
1158
|
-
const emittedBlocks = [];
|
|
1159
|
-
const toolBlocks = /* @__PURE__ */ new Map();
|
|
1160
|
-
const toolNames = /* @__PURE__ */ new Map();
|
|
1161
|
-
let usage;
|
|
1162
|
-
let finish = { kind: "stop" };
|
|
1163
|
-
const toolIdOf = (payload) => {
|
|
1164
|
-
const id = payload?.item_id ?? payload?.call_id ?? payload?.id;
|
|
1165
|
-
return typeof id === "string" && id ? id : void 0;
|
|
1166
|
-
};
|
|
1167
|
-
const learnToolName = (id, name2) => {
|
|
1168
|
-
if (typeof id !== "string" || !id || typeof name2 !== "string" || !name2) return;
|
|
1169
|
-
toolNames.set(id, name2);
|
|
1170
|
-
const tool = toolBlocks.get(id);
|
|
1171
|
-
if (tool) tool.name = name2;
|
|
1172
|
-
};
|
|
1173
|
-
const adoptToolArguments = (id, value) => {
|
|
1174
|
-
if (typeof id !== "string" || typeof value !== "string" || !value) return;
|
|
1175
|
-
const tool = toolBlocks.get(id);
|
|
1176
|
-
if (tool && value.length > tool.arguments.length) tool.arguments = value;
|
|
1177
|
-
};
|
|
1178
|
-
const flushSse = function* (raw) {
|
|
1179
|
-
const lines = raw.split("\n");
|
|
1180
|
-
let event = "message";
|
|
1181
|
-
const data = [];
|
|
1182
|
-
for (const line of lines) {
|
|
1183
|
-
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
1184
|
-
else if (line.startsWith("data:")) data.push(line.slice(5).trim());
|
|
1185
|
-
}
|
|
1186
|
-
const payloadText = data.join("\n");
|
|
1187
|
-
if (!payloadText || payloadText === "[DONE]") return;
|
|
1188
|
-
let payload;
|
|
1189
|
-
try {
|
|
1190
|
-
payload = JSON.parse(payloadText);
|
|
1191
|
-
} catch {
|
|
1192
|
-
return;
|
|
1193
|
-
}
|
|
1194
|
-
const type = payload.type ?? event;
|
|
1195
|
-
if (type === "response.output_text.delta" || type === "response.text.delta") {
|
|
1196
|
-
const delta = payload.delta ?? payload.text ?? "";
|
|
1197
|
-
if (!delta) return;
|
|
1198
|
-
if (textIndex === void 0) {
|
|
1199
|
-
textIndex = nextIndex++;
|
|
1200
|
-
emittedBlocks[textIndex] = { type: "text" };
|
|
1201
|
-
yield { type: "block-start", index: textIndex, blockType: "text" };
|
|
1202
|
-
}
|
|
1203
|
-
textContent += delta;
|
|
1204
|
-
yield { type: "text-delta", index: textIndex, text: String(delta) };
|
|
1205
|
-
return;
|
|
1206
|
-
}
|
|
1207
|
-
if (type === "response.reasoning_text.delta" || type === "response.reasoning.delta" || type === "response.reasoning_summary_text.delta") {
|
|
1208
|
-
const delta = payload.delta ?? payload.text ?? "";
|
|
1209
|
-
if (!delta) return;
|
|
1210
|
-
if (reasoningIndex === void 0) {
|
|
1211
|
-
reasoningIndex = nextIndex++;
|
|
1212
|
-
emittedBlocks[reasoningIndex] = { type: "reasoning" };
|
|
1213
|
-
yield { type: "block-start", index: reasoningIndex, blockType: "reasoning" };
|
|
1214
|
-
}
|
|
1215
|
-
reasoningContent += delta;
|
|
1216
|
-
yield { type: "reasoning-delta", index: reasoningIndex, text: String(delta) };
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
if (type === "response.output_item.added" || type === "response.output_item.done") {
|
|
1220
|
-
const item = payload.item;
|
|
1221
|
-
if (item?.type === "reasoning" && typeof item.encrypted_content === "string" && item.encrypted_content) {
|
|
1222
|
-
if (reasoningIndex === void 0) {
|
|
1223
|
-
reasoningIndex = nextIndex++;
|
|
1224
|
-
emittedBlocks[reasoningIndex] = { type: "reasoning" };
|
|
1225
|
-
yield { type: "block-start", index: reasoningIndex, blockType: "reasoning" };
|
|
1226
|
-
}
|
|
1227
|
-
emittedBlocks[reasoningIndex] = { type: "reasoning", item };
|
|
1228
|
-
}
|
|
1229
|
-
if (item?.type === "function_call") {
|
|
1230
|
-
learnToolName(item.id, item.name);
|
|
1231
|
-
learnToolName(item.call_id, item.name);
|
|
1232
|
-
adoptToolArguments(item.id, item.arguments);
|
|
1233
|
-
}
|
|
1234
|
-
return;
|
|
1235
|
-
}
|
|
1236
|
-
if (type === "response.function_call_arguments.done") {
|
|
1237
|
-
const id = toolIdOf(payload);
|
|
1238
|
-
learnToolName(id, payload.name);
|
|
1239
|
-
adoptToolArguments(id, payload.arguments);
|
|
1240
|
-
return;
|
|
1241
|
-
}
|
|
1242
|
-
if (type === "response.function_call_arguments.delta") {
|
|
1243
|
-
const id = toolIdOf(payload);
|
|
1244
|
-
if (!id) return;
|
|
1245
|
-
let tool = toolBlocks.get(id);
|
|
1246
|
-
if (!tool) {
|
|
1247
|
-
tool = {
|
|
1248
|
-
index: nextIndex++,
|
|
1249
|
-
id,
|
|
1250
|
-
// Never leave this undefined: an unserializable chunk aborts the turn.
|
|
1251
|
-
name: toolNames.get(id) ?? "tool",
|
|
1252
|
-
arguments: ""
|
|
1253
|
-
};
|
|
1254
|
-
emittedBlocks[tool.index] = { type: "tool-call" };
|
|
1255
|
-
toolBlocks.set(id, tool);
|
|
1256
|
-
yield { type: "block-start", index: tool.index, blockType: "tool-call" };
|
|
1257
|
-
}
|
|
1258
|
-
const delta = payload.delta ?? payload.arguments ?? "";
|
|
1259
|
-
tool.arguments += delta;
|
|
1260
|
-
yield { type: "tool-call-delta", index: tool.index, id, name: tool.name, argumentsDelta: String(delta) };
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
if (type === "response.completed") {
|
|
1264
|
-
for (const item of payload.response?.output ?? []) {
|
|
1265
|
-
if (item?.type !== "function_call") continue;
|
|
1266
|
-
learnToolName(item.id, item.name);
|
|
1267
|
-
learnToolName(item.call_id, item.name);
|
|
1268
|
-
const id = typeof item.id === "string" && item.id ? item.id : void 0;
|
|
1269
|
-
if (id && !toolBlocks.has(id)) {
|
|
1270
|
-
const tool = { index: nextIndex++, id, name: toolNames.get(id) ?? "tool", arguments: "" };
|
|
1271
|
-
toolBlocks.set(id, tool);
|
|
1272
|
-
emittedBlocks[tool.index] = { type: "tool-call" };
|
|
1273
|
-
yield { type: "block-start", index: tool.index, blockType: "tool-call" };
|
|
1274
|
-
}
|
|
1275
|
-
adoptToolArguments(id, item.arguments);
|
|
1276
|
-
}
|
|
1277
|
-
const responseUsage = payload.response?.usage ?? payload.usage;
|
|
1278
|
-
if (responseUsage) {
|
|
1279
|
-
const details = responseUsage.input_tokens_details ?? responseUsage.prompt_tokens_details ?? {};
|
|
1280
|
-
const cachedTokens = finiteOr(details.cached_tokens, 0);
|
|
1281
|
-
const cacheWriteTokens = finiteOr(details.cache_write_tokens, 0);
|
|
1282
|
-
const promptTotal = finiteOr(
|
|
1283
|
-
responseUsage.input_tokens ?? responseUsage.prompt_tokens,
|
|
1284
|
-
0
|
|
1285
|
-
);
|
|
1286
|
-
const outputTokens = finiteOr(
|
|
1287
|
-
responseUsage.output_tokens ?? responseUsage.completion_tokens,
|
|
1288
|
-
0
|
|
1289
|
-
);
|
|
1290
|
-
usage = {
|
|
1291
|
-
// The provider folds cache hits into the prompt count, while DSH's
|
|
1292
|
-
// fields are disjoint: billed input = inputTokens + cacheRead +
|
|
1293
|
-
// cacheWrite. Subtracting is what makes the host's "cache hit" figure
|
|
1294
|
-
// real instead of a permanent 0%. Both cache fields stay numeric so
|
|
1295
|
-
// consumers can always add them up.
|
|
1296
|
-
inputTokens: Math.max(0, promptTotal - cachedTokens - cacheWriteTokens),
|
|
1297
|
-
outputTokens,
|
|
1298
|
-
cacheReadTokens: cachedTokens,
|
|
1299
|
-
cacheWriteTokens
|
|
1300
|
-
};
|
|
1301
|
-
const reasoningTokens = finiteOr(responseUsage.output_tokens_details?.reasoning_tokens, void 0);
|
|
1302
|
-
if (reasoningTokens !== void 0) usage.reasoningTokens = reasoningTokens;
|
|
1303
|
-
usage.totalTokens = finiteOr(responseUsage.total_tokens, promptTotal + outputTokens);
|
|
1304
|
-
}
|
|
1305
|
-
finish = mapFinish(payload.response?.status === "incomplete" ? "length" : "stop");
|
|
1306
|
-
if (toolBlocks.size) finish = { kind: "tool-calls" };
|
|
1307
|
-
}
|
|
1308
|
-
if (type === "response.failed" || type === "error") {
|
|
1309
|
-
const raw2 = payload.error?.message ?? payload.message;
|
|
1310
|
-
const failure = {
|
|
1311
|
-
message: typeof raw2 === "string" && raw2 ? raw2 : "Grok Build stream failed",
|
|
1312
|
-
code: "PROVIDER"
|
|
1313
|
-
};
|
|
1314
|
-
const status = finiteOr(payload.error?.status, void 0);
|
|
1315
|
-
if (status !== void 0) failure.status = status;
|
|
1316
|
-
finish = { kind: "error", failure };
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
for await (const chunk of response.body) {
|
|
1320
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
1321
|
-
let separator;
|
|
1322
|
-
while ((separator = buffer.indexOf("\n\n")) !== -1) {
|
|
1323
|
-
const raw = buffer.slice(0, separator);
|
|
1324
|
-
buffer = buffer.slice(separator + 2);
|
|
1325
|
-
yield* flushSse(raw);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
if (buffer.trim()) yield* flushSse(buffer);
|
|
1329
|
-
if (reasoningIndex !== void 0) {
|
|
1330
|
-
yield { type: "block-end", index: reasoningIndex, block: { type: "reasoning", text: reasoningContent } };
|
|
1331
|
-
}
|
|
1332
|
-
if (textIndex !== void 0) {
|
|
1333
|
-
yield { type: "block-end", index: textIndex, block: { type: "text", text: textContent } };
|
|
1334
|
-
}
|
|
1335
|
-
for (const tool of toolBlocks.values()) {
|
|
1336
|
-
yield {
|
|
1337
|
-
type: "block-end",
|
|
1338
|
-
index: tool.index,
|
|
1339
|
-
block: { type: "tool-call", id: tool.id, name: tool.name ?? "tool", arguments: tool.arguments }
|
|
1340
|
-
};
|
|
1341
|
-
}
|
|
1342
|
-
if (usage) yield { type: "usage", usage };
|
|
1343
|
-
const finishChunk = { type: "finish", reason: finish };
|
|
1344
|
-
if (finish.kind !== "error" && finish.kind !== "aborted" && emittedBlocks.length === nextIndex) {
|
|
1345
|
-
finishChunk.replayState = {
|
|
1346
|
-
response: { kind: REPLAY_KIND, version: REPLAY_VERSION },
|
|
1347
|
-
blocks: emittedBlocks
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
yield finishChunk;
|
|
1351
|
-
}
|
|
1352
|
-
async function* streamResponses(options, token) {
|
|
1353
|
-
for await (const chunk of streamResponsesUnsafe(options, token)) {
|
|
1354
|
-
const safe = jsonSafeChunk(chunk);
|
|
1355
|
-
if (safe) yield safe;
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
894
|
function visiblePiModels(session) {
|
|
1359
895
|
if (session.publicAccount()?.signedIn !== true) return [];
|
|
1360
896
|
return toPiModels(session.models()).map((model) => model.provider === PROVIDER_ID ? model : { ...model, provider: PROVIDER_ID });
|
|
1361
897
|
}
|
|
1362
|
-
function createDuckAdapter(session, adapterOptions = {}) {
|
|
1363
|
-
const providerInfo = () => ({ id: PROVIDER_ID, name: DISPLAY_NAME });
|
|
1364
|
-
const list = () => {
|
|
1365
|
-
const signedIn = session.publicAccount()?.signedIn === true;
|
|
1366
|
-
return signedIn ? toLlmModels(session.models()) : [];
|
|
1367
|
-
};
|
|
1368
|
-
return {
|
|
1369
|
-
providerInfo,
|
|
1370
|
-
providerRetryPolicy() {
|
|
1371
|
-
return void 0;
|
|
1372
|
-
},
|
|
1373
|
-
async listModels(provider) {
|
|
1374
|
-
if (provider !== PROVIDER_ID) return [];
|
|
1375
|
-
return list();
|
|
1376
|
-
},
|
|
1377
|
-
async resolveModel(provider, model) {
|
|
1378
|
-
const found = list().find((item) => item.id === model);
|
|
1379
|
-
const raw = session.publicAccount()?.signedIn === true ? session.models().find((item) => item.id === model) : void 0;
|
|
1380
|
-
const info = {
|
|
1381
|
-
provider,
|
|
1382
|
-
id: model,
|
|
1383
|
-
name: found?.name ?? model,
|
|
1384
|
-
// The host gates prompt attachment admission on resolveModelInfo, NOT on
|
|
1385
|
-
// listModels, so this must match the catalog's modality map exactly.
|
|
1386
|
-
inputModalities: supportsImageInput(model) ? ["text", "image"] : ["text"],
|
|
1387
|
-
context: { contextWindow: raw?.contextWindow ?? 5e5 }
|
|
1388
|
-
};
|
|
1389
|
-
const reasoning = reasoningInfoOf(raw);
|
|
1390
|
-
if (reasoning) info.reasoning = reasoning;
|
|
1391
|
-
else if (found?.reasoning) info.reasoning = found.reasoning;
|
|
1392
|
-
return info;
|
|
1393
|
-
},
|
|
1394
|
-
async prepareCall(provider, model, signal) {
|
|
1395
|
-
const resolved = await this.resolveModel(provider, model, signal);
|
|
1396
|
-
return {
|
|
1397
|
-
model: resolved,
|
|
1398
|
-
stream: (options) => this.stream(options)
|
|
1399
|
-
};
|
|
1400
|
-
},
|
|
1401
|
-
async *stream(options) {
|
|
1402
|
-
if (options.provider !== PROVIDER_ID) {
|
|
1403
|
-
yield { type: "finish", reason: { kind: "error", failure: { message: "Unknown provider", code: "NO_ADAPTER" } } };
|
|
1404
|
-
return;
|
|
1405
|
-
}
|
|
1406
|
-
const callOptions = typeof adapterOptions.resolveAttachments === "function" ? { ...options, resolveAttachments: adapterOptions.resolveAttachments } : options;
|
|
1407
|
-
let token = await session.currentToken();
|
|
1408
|
-
if (!token) {
|
|
1409
|
-
yield { type: "finish", reason: { kind: "error", failure: { message: "Grok subscription is not signed in", code: "MISSING_CREDENTIAL" } } };
|
|
1410
|
-
return;
|
|
1411
|
-
}
|
|
1412
|
-
for (let attempt = 0; ; attempt++) {
|
|
1413
|
-
let emitted = false;
|
|
1414
|
-
try {
|
|
1415
|
-
for await (const chunk of streamResponses(callOptions, token, callOptions.resolveAttachments)) {
|
|
1416
|
-
emitted = true;
|
|
1417
|
-
yield chunk;
|
|
1418
|
-
}
|
|
1419
|
-
return;
|
|
1420
|
-
} catch (error) {
|
|
1421
|
-
const aborted = options.signal?.aborted === true;
|
|
1422
|
-
const status = finiteOr(error?.status, void 0);
|
|
1423
|
-
const canRetry = !aborted && !emitted && attempt === 0 && status === 401 && typeof session.refreshToken === "function";
|
|
1424
|
-
if (canRetry) {
|
|
1425
|
-
const renewed = await session.refreshToken();
|
|
1426
|
-
if (renewed && renewed !== token) {
|
|
1427
|
-
token = renewed;
|
|
1428
|
-
continue;
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
const failure = {
|
|
1432
|
-
message: status === 401 ? "Grok Build session is expired or unauthorized (HTTP 401). Run grok login, then Pull from Grok CLI." : error instanceof Error ? error.message : "Grok Build request failed",
|
|
1433
|
-
code: aborted ? "ABORTED" : status === 401 ? "UNAUTHORIZED" : "PROVIDER"
|
|
1434
|
-
};
|
|
1435
|
-
if (status !== void 0) failure.status = status;
|
|
1436
|
-
yield { type: "finish", reason: { kind: aborted ? "aborted" : "error", failure } };
|
|
1437
|
-
return;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
};
|
|
1442
|
-
}
|
|
1443
898
|
function createStore(session) {
|
|
1444
899
|
return {
|
|
1445
900
|
async read(providerId) {
|
|
@@ -1473,52 +928,23 @@ function buildAuthConfig() {
|
|
|
1473
928
|
}
|
|
1474
929
|
};
|
|
1475
930
|
}
|
|
1476
|
-
function
|
|
1477
|
-
const inject2 = (options) =>
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
...options
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
931
|
+
function withGrokRequestSemantics(api) {
|
|
932
|
+
const inject2 = (options) => {
|
|
933
|
+
const cacheKey = options?.cacheRetention === "none" ? void 0 : promptCacheKey(options);
|
|
934
|
+
return {
|
|
935
|
+
...options,
|
|
936
|
+
samplingParams: {
|
|
937
|
+
...options?.samplingParams,
|
|
938
|
+
include: ["reasoning.encrypted_content"],
|
|
939
|
+
...cacheKey === void 0 ? {} : { prompt_cache_key: cacheKey }
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
};
|
|
1484
943
|
return {
|
|
1485
944
|
stream: (model, context, options) => api.stream(model, context, inject2(options)),
|
|
1486
945
|
streamSimple: (model, context, options) => api.streamSimple(model, context, inject2(options))
|
|
1487
946
|
};
|
|
1488
947
|
}
|
|
1489
|
-
function wrapAsHostAdapter(candidate, dshLlm) {
|
|
1490
|
-
if (!dshLlm?.LlmAdapter || candidate instanceof dshLlm.LlmAdapter) return candidate;
|
|
1491
|
-
class GrokBuildAdapter extends dshLlm.LlmAdapter {
|
|
1492
|
-
providerInfo(provider) {
|
|
1493
|
-
return candidate.providerInfo(provider);
|
|
1494
|
-
}
|
|
1495
|
-
providerRetryPolicy(provider) {
|
|
1496
|
-
return candidate.providerRetryPolicy(provider);
|
|
1497
|
-
}
|
|
1498
|
-
listModels(provider) {
|
|
1499
|
-
return candidate.listModels(provider);
|
|
1500
|
-
}
|
|
1501
|
-
resolveModel(provider, model, signal) {
|
|
1502
|
-
return candidate.resolveModel(provider, model, signal);
|
|
1503
|
-
}
|
|
1504
|
-
prepareCall(provider, model, signal) {
|
|
1505
|
-
return candidate.prepareCall(provider, model, signal);
|
|
1506
|
-
}
|
|
1507
|
-
stream(options) {
|
|
1508
|
-
return candidate.stream(options);
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
return new GrokBuildAdapter();
|
|
1512
|
-
}
|
|
1513
|
-
function createGrokBuildAdapterSync(session, options = {}) {
|
|
1514
|
-
const duck = createDuckAdapter(session, { resolveAttachments: options.resolveAttachments });
|
|
1515
|
-
const dshLlm = options.dshLlm;
|
|
1516
|
-
return {
|
|
1517
|
-
adapter: wrapAsHostAdapter(duck, dshLlm),
|
|
1518
|
-
kind: "custom-mvp",
|
|
1519
|
-
note: "Registered a sync duck Responses adapter; pi-ai upgrade may follow in the background."
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
948
|
async function createGrokBuildAdapter(session, options = {}) {
|
|
1523
949
|
const importOpts = options.importOptions ?? {};
|
|
1524
950
|
const [piAi, dshPi, dshLlm] = await Promise.all([
|
|
@@ -1526,13 +952,11 @@ async function createGrokBuildAdapter(session, options = {}) {
|
|
|
1526
952
|
optionalImport("@deepseek-ai/dsh-llm-pi-ai", importOpts),
|
|
1527
953
|
optionalImport("@deepseek-ai/dsh-llm", importOpts)
|
|
1528
954
|
]);
|
|
1529
|
-
const duck = createDuckAdapter(session, { resolveAttachments: options.resolveAttachments });
|
|
1530
|
-
const asHostAdapter = (candidate) => wrapAsHostAdapter(candidate, dshLlm);
|
|
1531
955
|
if (!piAi?.createProvider || !dshPi?.PiAiAdapter) {
|
|
1532
956
|
return {
|
|
1533
|
-
adapter:
|
|
1534
|
-
kind: "
|
|
1535
|
-
note: "
|
|
957
|
+
adapter: void 0,
|
|
958
|
+
kind: "unavailable",
|
|
959
|
+
note: "the host pi-ai adapter is unavailable: @earendil-works/pi-ai and @deepseek-ai/dsh-llm-pi-ai must be resolvable from the DSH install"
|
|
1536
960
|
};
|
|
1537
961
|
}
|
|
1538
962
|
let responsesApi;
|
|
@@ -1543,9 +967,13 @@ async function createGrokBuildAdapter(session, options = {}) {
|
|
|
1543
967
|
responsesApi = void 0;
|
|
1544
968
|
}
|
|
1545
969
|
if (!responsesApi) {
|
|
1546
|
-
return {
|
|
970
|
+
return {
|
|
971
|
+
adapter: void 0,
|
|
972
|
+
kind: "unavailable",
|
|
973
|
+
note: "the pi-ai openai-responses API module is unavailable in this host"
|
|
974
|
+
};
|
|
1547
975
|
}
|
|
1548
|
-
responsesApi =
|
|
976
|
+
responsesApi = withGrokRequestSemantics(responsesApi);
|
|
1549
977
|
const store = createStore(session);
|
|
1550
978
|
const authModels = piAi.createModels({
|
|
1551
979
|
credentials: store,
|
|
@@ -2729,31 +2157,17 @@ function apply(ctx, options = {}) {
|
|
|
2729
2157
|
logger: ctx.logger,
|
|
2730
2158
|
onCatalogChange: notifyCatalogChange
|
|
2731
2159
|
});
|
|
2732
|
-
const adapterState = { kind: "
|
|
2160
|
+
const adapterState = { kind: "starting" };
|
|
2733
2161
|
const pluginManager = options.pluginManager ?? createGrokPluginManager();
|
|
2734
2162
|
const handler = createRpcHandler(session, {
|
|
2735
2163
|
pluginManager,
|
|
2736
2164
|
diagnostics: () => ({
|
|
2737
|
-
adapter: adapterState.
|
|
2738
|
-
adapterKind: adapterState.kind,
|
|
2165
|
+
adapter: adapterState.kind,
|
|
2739
2166
|
imageInput: typeof resolveAttachments()?.readImageRequest === "function",
|
|
2740
|
-
hostPeersResolved: adapterState.
|
|
2167
|
+
hostPeersResolved: adapterState.kind === "pi-ai"
|
|
2741
2168
|
})
|
|
2742
2169
|
});
|
|
2743
2170
|
const resolveAttachments = attachmentResolver(ctx);
|
|
2744
|
-
const createSync = options.createSync ?? createGrokBuildAdapterSync;
|
|
2745
|
-
try {
|
|
2746
|
-
const created = createSync(session, { resolveAttachments });
|
|
2747
|
-
if (created.note) ctx.logger?.debug?.(created.note);
|
|
2748
|
-
adapterState.kind = created.kind;
|
|
2749
|
-
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
2750
|
-
notifyCatalogChange();
|
|
2751
|
-
} catch (error) {
|
|
2752
|
-
ctx.logger?.warn?.(
|
|
2753
|
-
"Grok subscription duck adapter failed to register: %s",
|
|
2754
|
-
error instanceof Error ? error.message : "unknown"
|
|
2755
|
-
);
|
|
2756
|
-
}
|
|
2757
2171
|
const defer = options.defer ?? scheduleDeferred2;
|
|
2758
2172
|
defer(() => {
|
|
2759
2173
|
void deferredBoot(ctx, session, notifyCatalogChange, options, adapterState);
|
|
@@ -2788,24 +2202,32 @@ async function deferredBoot(ctx, session, notifyCatalogChange, options = {}, ada
|
|
|
2788
2202
|
);
|
|
2789
2203
|
});
|
|
2790
2204
|
}
|
|
2791
|
-
if (options.
|
|
2792
|
-
|
|
2205
|
+
if (options.skipAdapter) {
|
|
2206
|
+
adapterState.kind = "unavailable";
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
const buildAdapter = options.buildAdapter ?? createGrokBuildAdapter;
|
|
2793
2210
|
try {
|
|
2794
|
-
const created = await
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2211
|
+
const created = await buildAdapter(session, {
|
|
2212
|
+
...options.adapterOptions,
|
|
2213
|
+
resolveAttachments: attachmentResolver(ctx)
|
|
2214
|
+
});
|
|
2215
|
+
if (!created?.adapter) {
|
|
2216
|
+
adapterState.kind = "unavailable";
|
|
2217
|
+
ctx.logger?.warn?.(
|
|
2218
|
+
"Grok subscription adapter is unavailable: %s",
|
|
2219
|
+
created?.note ?? "unknown"
|
|
2220
|
+
);
|
|
2221
|
+
return;
|
|
2805
2222
|
}
|
|
2223
|
+
if (created.note) ctx.logger?.warn?.(created.note);
|
|
2224
|
+
ctx.llm.registerAdapter([PROVIDER_ID], created.adapter);
|
|
2225
|
+
adapterState.kind = created.kind ?? "pi-ai";
|
|
2226
|
+
notifyCatalogChange?.();
|
|
2806
2227
|
} catch (error) {
|
|
2228
|
+
adapterState.kind = "unavailable";
|
|
2807
2229
|
ctx.logger?.warn?.(
|
|
2808
|
-
"Grok subscription adapter
|
|
2230
|
+
"Grok subscription adapter registration failed: %s",
|
|
2809
2231
|
error instanceof Error ? error.message : "unknown"
|
|
2810
2232
|
);
|
|
2811
2233
|
}
|