@adaptiqai/deepseek-harness 0.2.12 → 0.2.14
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/README.md +8 -7
- package/lib/client.js +4 -4
- package/lib/host.js +372 -178
- package/lib/types/client/SettingsPage.d.ts +1 -1
- package/lib/types/client/pill.d.ts +1 -1
- package/lib/types/host/adapter.d.ts +15 -0
- package/lib/types/host/images.d.ts +91 -0
- package/lib/types/host/wire.d.ts +25 -7
- package/package.json +10 -10
package/lib/host.js
CHANGED
|
@@ -54,7 +54,7 @@ import { attributionHeaders as attributionHeaders2 } from "@deepseek-ai/dsh-llm"
|
|
|
54
54
|
import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
|
|
55
55
|
|
|
56
56
|
// src/host/adapter.ts
|
|
57
|
-
import { attributionHeaders, LlmAdapter, LlmError, ProviderRequestId } from "@deepseek-ai/dsh-llm";
|
|
57
|
+
import { attributionHeaders, IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, ProviderRequestId } from "@deepseek-ai/dsh-llm";
|
|
58
58
|
|
|
59
59
|
// ../core/lib/errors.js
|
|
60
60
|
var CODES = {
|
|
@@ -122,27 +122,32 @@ function classifyFailure(status, raw, headers, model2, options = {}) {
|
|
|
122
122
|
const library = /\blibrary is full/iu.test(detail.message ?? "");
|
|
123
123
|
return { ...make(CODES.memoryFull, library ? MEMORY_FULL_LIBRARY : MEMORY_FULL, "memory-full"), memoryFull: library ? "library" : "no-room" };
|
|
124
124
|
}
|
|
125
|
+
if (detail.code === "plan_usage_exhausted") {
|
|
126
|
+
const renewsAt = envelope.detail?.renews_at;
|
|
127
|
+
const failure = make(CODES.noLanes, detail.message ?? "Your plan's usage is used up for now. Pay per token from your balance in the meantime.", "no-lanes");
|
|
128
|
+
return typeof renewsAt === "number" ? { ...failure, planRenewsAt: renewsAt } : failure;
|
|
129
|
+
}
|
|
125
130
|
if (effective === 402 || detail.code === "rental_paused") {
|
|
126
131
|
const releaseAt = envelope.detail?.release_at;
|
|
127
132
|
const minutes = typeof releaseAt === "number" ? Math.max(1, Math.ceil((releaseAt * 1e3 - Date.now()) / 6e4)) : null;
|
|
128
|
-
const keep = minutes === null ? "to continue" : `within ${minutes} minute${minutes === 1 ? "" : "s"} to keep
|
|
129
|
-
const failure = make(CODES.outOfCredit, `Your balance ran out
|
|
133
|
+
const keep = minutes === null ? "to continue" : `within ${minutes} minute${minutes === 1 ? "" : "s"} to keep your models running`;
|
|
134
|
+
const failure = make(CODES.outOfCredit, `Your balance ran out. Add funds ${keep}.`, "out-of-credit");
|
|
130
135
|
return minutes === null ? failure : { ...failure, minutesLeft: minutes };
|
|
131
136
|
}
|
|
132
137
|
if (effective === 401) {
|
|
133
138
|
return make(CODES.signIn, "Your AdaptiqAI sign-in has expired. Open Settings \u2192 AdaptiqAI and sign in again.", "sign-in");
|
|
134
139
|
}
|
|
135
140
|
if (detail.code === "rental_stopped_unused") {
|
|
136
|
-
return make(CODES.noLanes,
|
|
141
|
+
return make(CODES.noLanes, "The model stopped after going unused. Get access to start it again.", "no-lanes");
|
|
137
142
|
}
|
|
138
143
|
if (has(detail, "AdaptiqLaneAccessRequired", "inference_entitlement_required", "instance_access_required", "lane_access") || effective === 403 && has(detail, "lane", "entitle", "model access")) {
|
|
139
|
-
return make(CODES.noLanes,
|
|
144
|
+
return make(CODES.noLanes, "This model isn't in your workspace yet. Get access to continue; you pay per token from your balance.", "no-lanes");
|
|
140
145
|
}
|
|
141
146
|
if (effective === 403) {
|
|
142
147
|
return make(CODES.signIn, "This AdaptiqAI connection isn't allowed to do that. Sign in again from Settings \u2192 AdaptiqAI.", "sign-in");
|
|
143
148
|
}
|
|
144
149
|
if (effective === 429 || has(detail, "AdaptiqLaneQueueTimeout", "InflightLimit", "rate_limit")) {
|
|
145
|
-
return make(CODES.busy,
|
|
150
|
+
return make(CODES.busy, `${name2} is busy. Your request will retry automatically in a moment.`, "busy");
|
|
146
151
|
}
|
|
147
152
|
if (effective === 503 && detail.code !== null && MODEL_OFFLINE_CODES.includes(detail.code)) {
|
|
148
153
|
return make(CODES.server, `${model2 ?? "This model"} is offline right now. It will retry automatically.`, "retrying");
|
|
@@ -154,7 +159,7 @@ function classifyFailure(status, raw, headers, model2, options = {}) {
|
|
|
154
159
|
return make(CODES.conversationUnavailable, "This conversation couldn't be opened on AdaptiqAI right now. Try again shortly; your saved memory is unchanged.", null);
|
|
155
160
|
}
|
|
156
161
|
if (effective === 404 && (has(detail, "model", "not_found", "does not exist") || detail.message === null)) {
|
|
157
|
-
return make(CODES.modelNotFound, `${name2} isn't available to your workspace right now. Pick another AdaptiqAI model, or check your
|
|
162
|
+
return make(CODES.modelNotFound, `${name2} isn't available to your workspace right now. Pick another AdaptiqAI model, or check your models in the console.`, "model-unavailable");
|
|
158
163
|
}
|
|
159
164
|
if (effective === 400 || effective === 413 || effective === 422) {
|
|
160
165
|
const said = detail.message !== null ? ` (${detail.message.slice(0, 200)})` : "";
|
|
@@ -321,10 +326,10 @@ var REFRESH_MARGIN_MS = 2 * 6e4;
|
|
|
321
326
|
var DEVICE_TIMEOUT_MS = 10 * 6e4;
|
|
322
327
|
var UNREACHABLE = "AdaptiqAI can't be reached. Check your internet connection and try again.";
|
|
323
328
|
function deviceLabel(value) {
|
|
324
|
-
let
|
|
325
|
-
while (Buffer.byteLength(
|
|
326
|
-
|
|
327
|
-
return
|
|
329
|
+
let label2 = value.replace(/\.local$/iu, "").replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/gu, " ").trim() || "this computer";
|
|
330
|
+
while (Buffer.byteLength(label2) > 80)
|
|
331
|
+
label2 = label2.slice(0, -1);
|
|
332
|
+
return label2;
|
|
328
333
|
}
|
|
329
334
|
var defaultSleep = (ms, signal) => new Promise((resolve2, reject) => {
|
|
330
335
|
if (signal.aborted) {
|
|
@@ -1061,13 +1066,11 @@ var en = {
|
|
|
1061
1066
|
expiredTitle: "Your sign-in expired",
|
|
1062
1067
|
signInAgain: "Sign in again",
|
|
1063
1068
|
tryAgain: "Try again",
|
|
1064
|
-
models: "Models
|
|
1069
|
+
models: "Models",
|
|
1065
1070
|
status_running: "Running",
|
|
1066
1071
|
status_offline: "Offline",
|
|
1067
1072
|
status_disabled: "Paused",
|
|
1068
1073
|
status_unknown: "Available",
|
|
1069
|
-
lanesOne: "{count} lane",
|
|
1070
|
-
lanesMany: "{count} lanes",
|
|
1071
1074
|
thinkingLevels: "Thinking {levels}",
|
|
1072
1075
|
noModels: "Pick your first model",
|
|
1073
1076
|
getAccess: "Get access",
|
|
@@ -1093,10 +1096,10 @@ var en = {
|
|
|
1093
1096
|
routeConflictTitle: "AdaptiqAI models are hidden",
|
|
1094
1097
|
pillMemory: "Memory",
|
|
1095
1098
|
pillPreparing: "Preparing memory",
|
|
1096
|
-
pillQueued: "Waiting
|
|
1099
|
+
pillQueued: "Waiting in line",
|
|
1097
1100
|
pillStarting: "Starting\u2026",
|
|
1098
1101
|
pillSignIn: "Sign in to AdaptiqAI",
|
|
1099
|
-
pillFromMemory: "{tokens} from memory",
|
|
1102
|
+
pillFromMemory: "{tokens} from memory \xB7 free",
|
|
1100
1103
|
pillTooltip: "AdaptiqAI memory for this conversation",
|
|
1101
1104
|
popTitle: "Memory",
|
|
1102
1105
|
popSaving: "Saving",
|
|
@@ -1172,22 +1175,22 @@ var en = {
|
|
|
1172
1175
|
accessTitle: "Get a model",
|
|
1173
1176
|
offerSpeed: "{speed} tokens/s",
|
|
1174
1177
|
offerMemory: "{tokens} memory",
|
|
1175
|
-
offerPrice: "{price} per
|
|
1178
|
+
offerPrice: "{price} per {period}",
|
|
1176
1179
|
offerOwned: "In your workspace",
|
|
1177
1180
|
offerFull: "Full right now",
|
|
1178
1181
|
period_hour: "hour",
|
|
1179
1182
|
period_month: "month",
|
|
1180
1183
|
period_once: "purchase",
|
|
1181
|
-
lanesLabel: "
|
|
1182
|
-
lanesHint: "
|
|
1184
|
+
lanesLabel: "At once",
|
|
1185
|
+
lanesHint: "How many conversations can run at the same time.",
|
|
1183
1186
|
totalPrice: "{price} per {period}",
|
|
1184
1187
|
noOffers: "No models are on offer right now. Check back soon.",
|
|
1185
|
-
manualNote: "Our team
|
|
1186
|
-
balanceNote: "
|
|
1188
|
+
manualNote: "Our team turns it on after you confirm.",
|
|
1189
|
+
balanceNote: "You pay per token from your balance. Reads from memory are free.",
|
|
1187
1190
|
cardNote: "You pay by card in your browser.",
|
|
1188
|
-
addLanes: "
|
|
1191
|
+
addLanes: "Run more at once",
|
|
1189
1192
|
purchase_approving: "Confirm in your browser",
|
|
1190
|
-
purchase_approving_body: "We opened a page to confirm {
|
|
1193
|
+
purchase_approving_body: "We opened a page to confirm {model}. This updates by itself.",
|
|
1191
1194
|
purchase_console_body: "Finish on the page we opened. {model} appears here as soon as it's active.",
|
|
1192
1195
|
purchase_requested: "Requested. We'll activate it shortly",
|
|
1193
1196
|
purchase_requested_body: "You'll get an email too. {model} appears here by itself.",
|
|
@@ -1197,10 +1200,8 @@ var en = {
|
|
|
1197
1200
|
purchase_ready_body: "Pick it in the model picker and start.",
|
|
1198
1201
|
purchase_denied: "The purchase was declined in the browser.",
|
|
1199
1202
|
purchase_expired: "The confirmation page expired.",
|
|
1200
|
-
purchase_stopped: "The
|
|
1203
|
+
purchase_stopped: "The model has stopped.",
|
|
1201
1204
|
purchase_failed: "The purchase didn't go through.",
|
|
1202
|
-
lanesCount: "{count} lanes",
|
|
1203
|
-
laneCount: "1 lane",
|
|
1204
1205
|
openPage: "Open the page again",
|
|
1205
1206
|
stopWaiting: "Stop waiting",
|
|
1206
1207
|
startChatting: "Start a conversation",
|
|
@@ -1274,17 +1275,21 @@ var en = {
|
|
|
1274
1275
|
n_connected_body: "Your AdaptiqAI models are in the model picker.",
|
|
1275
1276
|
n_compaction_title: "Nothing to compact",
|
|
1276
1277
|
n_compaction_body: "AdaptiqAI keeps the whole conversation in memory, so nothing needs shortening.",
|
|
1278
|
+
n_imagesTextOnly_title: "Images were not sent",
|
|
1279
|
+
n_imagesTextOnly_body: "{model} reads text only, so the model got a short note in place of each image. Choose a model that reads images to ask about them.",
|
|
1277
1280
|
n_signIn_title: "Sign in to keep going",
|
|
1278
1281
|
n_signIn_body: "Your AdaptiqAI connection needs a fresh sign-in.",
|
|
1279
1282
|
n_connect_body: "Connect your AdaptiqAI account to use this model.",
|
|
1280
|
-
n_noLanes_title: "
|
|
1281
|
-
n_noLanes_body: "
|
|
1282
|
-
n_laneStopped_body: "
|
|
1283
|
+
n_noLanes_title: "Get access to continue",
|
|
1284
|
+
n_noLanes_body: "This model isn't in your workspace yet. You pay per token from your balance; reads from memory are free.",
|
|
1285
|
+
n_laneStopped_body: "The model stopped after going unused. Start it again to continue.",
|
|
1286
|
+
n_planUsedUp_body: "Your plan's usage is used up until {until}. Pay per token from your balance in the meantime.",
|
|
1287
|
+
n_planUsedUpNoDate_body: "Your plan's usage is used up for now. Pay per token from your balance in the meantime.",
|
|
1283
1288
|
n_credit_title: "Add funds to continue",
|
|
1284
|
-
n_credit_body: "Your balance ran out
|
|
1285
|
-
n_creditMinutes_body: "Your balance ran out
|
|
1286
|
-
n_busy_title: "
|
|
1287
|
-
n_busy_body: "Your request
|
|
1289
|
+
n_credit_body: "Your balance ran out. Add funds to keep going.",
|
|
1290
|
+
n_creditMinutes_body: "Your balance ran out. Add funds within {minutes} min to keep your models running.",
|
|
1291
|
+
n_busy_title: "{model} is busy",
|
|
1292
|
+
n_busy_body: "Your request is waiting in line and retries by itself.",
|
|
1288
1293
|
n_preparing_title: "Getting your conversation ready",
|
|
1289
1294
|
n_preparing_body: "AdaptiqAI is loading this conversation's memory. It retries by itself.",
|
|
1290
1295
|
n_unavailable_title: "{model} isn't available",
|
|
@@ -1323,122 +1328,6 @@ function fill(template, params = {}) {
|
|
|
1323
1328
|
return template.replace(/\{(\w+)\}/gu, (whole, name2) => name2 in params ? String(params[name2]) : whole);
|
|
1324
1329
|
}
|
|
1325
1330
|
|
|
1326
|
-
// ../core/lib/update.js
|
|
1327
|
-
var RELEASE_AGE_MS = 24 * 36e5;
|
|
1328
|
-
var RELEASE_VERSION = /^\d+\.\d+\.\d+$/u;
|
|
1329
|
-
function semverGreater(a, b) {
|
|
1330
|
-
const parse = (value) => value.replace(/^v/u, "").split(/[.-]/u).slice(0, 3).map((part) => Number.parseInt(part, 10) || 0);
|
|
1331
|
-
const [x, y] = [parse(a), parse(b)];
|
|
1332
|
-
for (let i = 0; i < 3; i++)
|
|
1333
|
-
if ((x[i] ?? 0) !== (y[i] ?? 0))
|
|
1334
|
-
return (x[i] ?? 0) > (y[i] ?? 0);
|
|
1335
|
-
return false;
|
|
1336
|
-
}
|
|
1337
|
-
var UpdateFailure = class extends Error {
|
|
1338
|
-
code;
|
|
1339
|
-
constructor(code, message) {
|
|
1340
|
-
super(message);
|
|
1341
|
-
this.code = code;
|
|
1342
|
-
this.name = "UpdateFailure";
|
|
1343
|
-
}
|
|
1344
|
-
};
|
|
1345
|
-
|
|
1346
|
-
// ../core/lib/usage.js
|
|
1347
|
-
async function* parseSse(body, activity) {
|
|
1348
|
-
const decoder = new TextDecoder();
|
|
1349
|
-
const reader = body.getReader();
|
|
1350
|
-
let buffer = "";
|
|
1351
|
-
try {
|
|
1352
|
-
while (true) {
|
|
1353
|
-
const { value, done } = await reader.read();
|
|
1354
|
-
if (done)
|
|
1355
|
-
break;
|
|
1356
|
-
activity();
|
|
1357
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1358
|
-
let boundary = nextBoundary(buffer);
|
|
1359
|
-
while (boundary !== null) {
|
|
1360
|
-
const block = buffer.slice(0, boundary.index);
|
|
1361
|
-
buffer = buffer.slice(boundary.index + boundary.length);
|
|
1362
|
-
const event = parseBlock(block);
|
|
1363
|
-
if (event !== null)
|
|
1364
|
-
yield event;
|
|
1365
|
-
boundary = nextBoundary(buffer);
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
buffer += decoder.decode();
|
|
1369
|
-
const tail = parseBlock(buffer);
|
|
1370
|
-
if (tail !== null)
|
|
1371
|
-
yield tail;
|
|
1372
|
-
} finally {
|
|
1373
|
-
reader.releaseLock();
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
function nextBoundary(buffer) {
|
|
1377
|
-
const match = /\r?\n\r?\n/u.exec(buffer);
|
|
1378
|
-
return match === null ? null : { index: match.index, length: match[0].length };
|
|
1379
|
-
}
|
|
1380
|
-
function parseBlock(block) {
|
|
1381
|
-
let event = null;
|
|
1382
|
-
const data = [];
|
|
1383
|
-
for (const line of block.split(/\r?\n/u)) {
|
|
1384
|
-
const notice = /^: ?adaptiq-memory (.*)$/u.exec(line);
|
|
1385
|
-
if (notice !== null) {
|
|
1386
|
-
event = "adaptiq.memory";
|
|
1387
|
-
data.push(notice[1]);
|
|
1388
|
-
continue;
|
|
1389
|
-
}
|
|
1390
|
-
if (line === "" || line.startsWith(":"))
|
|
1391
|
-
continue;
|
|
1392
|
-
const colon = line.indexOf(":");
|
|
1393
|
-
const field = colon === -1 ? line : line.slice(0, colon);
|
|
1394
|
-
const value = colon === -1 ? "" : line.slice(colon + 1).replace(/^ /u, "");
|
|
1395
|
-
if (field === "event")
|
|
1396
|
-
event = value;
|
|
1397
|
-
else if (field === "data")
|
|
1398
|
-
data.push(value);
|
|
1399
|
-
}
|
|
1400
|
-
return data.length === 0 ? null : { event, data: data.join("\n") };
|
|
1401
|
-
}
|
|
1402
|
-
function mapUsage(raw) {
|
|
1403
|
-
const n = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
|
|
1404
|
-
const details = typeof raw.prompt_tokens_details === "object" && raw.prompt_tokens_details !== null ? raw.prompt_tokens_details : {};
|
|
1405
|
-
const completion = typeof raw.completion_tokens_details === "object" && raw.completion_tokens_details !== null ? raw.completion_tokens_details : {};
|
|
1406
|
-
const prompt = n(raw.prompt_tokens);
|
|
1407
|
-
const output = n(raw.completion_tokens);
|
|
1408
|
-
const cached = Math.min(prompt, n(details.cached_tokens) || n(raw.prompt_cache_hit_tokens));
|
|
1409
|
-
const written = Math.min(prompt - cached, n(details.cache_write_tokens) || n(raw.cache_write_tokens) || n(raw.cache_creation_input_tokens));
|
|
1410
|
-
const reasoning2 = n(completion.reasoning_tokens);
|
|
1411
|
-
const total = n(raw.total_tokens);
|
|
1412
|
-
return {
|
|
1413
|
-
inputTokens: Math.max(0, prompt - cached - written),
|
|
1414
|
-
outputTokens: output,
|
|
1415
|
-
...cached > 0 ? { cacheReadTokens: cached } : {},
|
|
1416
|
-
...written > 0 ? { cacheWriteTokens: written } : {},
|
|
1417
|
-
...reasoning2 > 0 ? { reasoningTokens: reasoning2 } : {},
|
|
1418
|
-
...total > 0 ? { totalTokens: total } : prompt + output > 0 ? { totalTokens: prompt + output } : {}
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
function memoryNotice(raw) {
|
|
1422
|
-
let value = raw;
|
|
1423
|
-
if (typeof raw === "string") {
|
|
1424
|
-
try {
|
|
1425
|
-
value = JSON.parse(raw);
|
|
1426
|
-
} catch {
|
|
1427
|
-
return null;
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
if (typeof value !== "object" || value === null)
|
|
1431
|
-
return null;
|
|
1432
|
-
const notice = value;
|
|
1433
|
-
if (notice.kind !== "hibernated" && notice.kind !== "suggestion")
|
|
1434
|
-
return null;
|
|
1435
|
-
const sessions = Array.isArray(notice.sessions) ? notice.sessions.length : 0;
|
|
1436
|
-
if (sessions === 0)
|
|
1437
|
-
return null;
|
|
1438
|
-
const number = (field) => typeof field === "number" && Number.isFinite(field) && field >= 0 ? field : null;
|
|
1439
|
-
return { kind: notice.kind, sessions, tokens: number(notice.tokens), usageRatio: number(notice.usage_ratio) };
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
1331
|
// ../core/lib/workspace.js
|
|
1443
1332
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
1444
1333
|
|
|
@@ -1545,9 +1434,9 @@ var PurchaseFlow = class {
|
|
|
1545
1434
|
}
|
|
1546
1435
|
const limit = laneLimit(offer);
|
|
1547
1436
|
if (limit === 0)
|
|
1548
|
-
throw new Error(`${offer.name}
|
|
1437
|
+
throw new Error(`${offer.name} is full right now. Try again later, or pick another model.`);
|
|
1549
1438
|
if (!Number.isInteger(lanes) || lanes < offer.minLanes || lanes > limit) {
|
|
1550
|
-
throw new Error(`Choose between ${offer.minLanes} and ${limit}
|
|
1439
|
+
throw new Error(`Choose between ${offer.minLanes} and ${limit} conversations at once.`);
|
|
1551
1440
|
}
|
|
1552
1441
|
const base = {
|
|
1553
1442
|
offerId: offer.id,
|
|
@@ -1668,14 +1557,34 @@ var PurchaseFlow = class {
|
|
|
1668
1557
|
}
|
|
1669
1558
|
};
|
|
1670
1559
|
|
|
1560
|
+
// ../core/lib/update.js
|
|
1561
|
+
var RELEASE_AGE_MS = 24 * 36e5;
|
|
1562
|
+
var RELEASE_VERSION = /^\d+\.\d+\.\d+$/u;
|
|
1563
|
+
function semverGreater(a, b) {
|
|
1564
|
+
const parse = (value) => value.replace(/^v/u, "").split(/[.-]/u).slice(0, 3).map((part) => Number.parseInt(part, 10) || 0);
|
|
1565
|
+
const [x, y] = [parse(a), parse(b)];
|
|
1566
|
+
for (let i = 0; i < 3; i++)
|
|
1567
|
+
if ((x[i] ?? 0) !== (y[i] ?? 0))
|
|
1568
|
+
return (x[i] ?? 0) > (y[i] ?? 0);
|
|
1569
|
+
return false;
|
|
1570
|
+
}
|
|
1571
|
+
var UpdateFailure = class extends Error {
|
|
1572
|
+
code;
|
|
1573
|
+
constructor(code, message) {
|
|
1574
|
+
super(message);
|
|
1575
|
+
this.code = code;
|
|
1576
|
+
this.name = "UpdateFailure";
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1671
1580
|
// ../core/lib/workspace.js
|
|
1672
1581
|
var obj4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1673
1582
|
var str4 = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
1674
1583
|
function say(title, body, params = {}) {
|
|
1675
1584
|
return { title: fill(en[title], params), body: body === null ? "" : fill(en[body], params), text: { title, body, params } };
|
|
1676
1585
|
}
|
|
1677
|
-
function button(id,
|
|
1678
|
-
return { id, label: en[
|
|
1586
|
+
function button(id, label2, extra = {}) {
|
|
1587
|
+
return { id, label: en[label2], labelKey: label2, ...extra };
|
|
1679
1588
|
}
|
|
1680
1589
|
var count2 = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
1681
1590
|
var PRIVATE_HELD = "A private conversation is not kept on AdaptiqAI, so it can't use this before it exists there. Turn Remember on for this conversation to add it.";
|
|
@@ -1842,7 +1751,7 @@ function copyFailure(error, model2) {
|
|
|
1842
1751
|
case "model_mismatch":
|
|
1843
1752
|
return str4(detail.message) ?? `This collection can't be made available for ${model2}.`;
|
|
1844
1753
|
case "inference_entitlement_required":
|
|
1845
|
-
return
|
|
1754
|
+
return "This model isn't in your workspace yet. Get access to continue; you pay per token from your balance.";
|
|
1846
1755
|
case "collection_not_found":
|
|
1847
1756
|
return "This collection no longer exists.";
|
|
1848
1757
|
case "sources_not_exportable":
|
|
@@ -2733,13 +2642,13 @@ var Workspace = class {
|
|
|
2733
2642
|
this.publish();
|
|
2734
2643
|
}
|
|
2735
2644
|
/** A browser approval completed. */
|
|
2736
|
-
signedIn(
|
|
2645
|
+
signedIn(label2) {
|
|
2737
2646
|
if (this.state.defaultModel === void 0) {
|
|
2738
2647
|
this.state = { ...this.state, defaultModel: "pending" };
|
|
2739
2648
|
void this.persist();
|
|
2740
2649
|
}
|
|
2741
2650
|
this.clearNotices("sign-in");
|
|
2742
|
-
this.raise({ kind: "connected", tone: "success", ...say("connectedTo", "n_connected_body", { workspace:
|
|
2651
|
+
this.raise({ kind: "connected", tone: "success", ...say("connectedTo", "n_connected_body", { workspace: label2 }), actions: [] });
|
|
2743
2652
|
this.summaryMode = "unknown";
|
|
2744
2653
|
this.summaryMissingAt = null;
|
|
2745
2654
|
void this.refresh();
|
|
@@ -2821,7 +2730,9 @@ var Workspace = class {
|
|
|
2821
2730
|
/**
|
|
2822
2731
|
* Whether this conversation is known to be stored on AdaptiqAI. A private
|
|
2823
2732
|
* turn of a conversation that is not stored yet runs ephemeral: there is
|
|
2824
|
-
* nothing to read back and nothing may be saved.
|
|
2733
|
+
* nothing to read back and nothing may be saved. While the last answer was
|
|
2734
|
+
* "not stored", the server is asked again: a remembered turn since may have
|
|
2735
|
+
* stored it, and a plugin that reports no turns would not hear of it.
|
|
2825
2736
|
* @param sessionId - harness session id.
|
|
2826
2737
|
* @returns true when the server has the conversation.
|
|
2827
2738
|
*/
|
|
@@ -2829,7 +2740,7 @@ var Workspace = class {
|
|
|
2829
2740
|
if (sessionId2 === void 0)
|
|
2830
2741
|
return false;
|
|
2831
2742
|
const session = this.session(sessionId2);
|
|
2832
|
-
if (session.remoteAt === 0 && this.deps.auth.phase() === "connected") {
|
|
2743
|
+
if ((session.remoteAt === 0 || !session.exists) && this.deps.auth.phase() === "connected") {
|
|
2833
2744
|
await Promise.race([this.loadSession(session).catch(() => {
|
|
2834
2745
|
}), new Promise((resolve2) => setTimeout(resolve2, 2500))]);
|
|
2835
2746
|
}
|
|
@@ -3325,6 +3236,14 @@ var Workspace = class {
|
|
|
3325
3236
|
actions: [button("open-console", "n_openConsole", { url: this.links.console, primary: true })]
|
|
3326
3237
|
});
|
|
3327
3238
|
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Say that a message's images were not sent because the model reads text only.
|
|
3241
|
+
* @param sessionId - the conversation, when known.
|
|
3242
|
+
* @param modelName - the model's display name.
|
|
3243
|
+
*/
|
|
3244
|
+
imagesNotSent(sessionId2, modelName) {
|
|
3245
|
+
this.raise({ kind: "images", tone: "warning", ...say("n_imagesTextOnly_title", "n_imagesTextOnly_body", { model: modelName }), actions: [], ...sessionId2 === void 0 ? {} : { sessionId: sessionId2 } });
|
|
3246
|
+
}
|
|
3328
3247
|
failed(sessionId2, model2, failure) {
|
|
3329
3248
|
if (failure.code === CODES.compaction) {
|
|
3330
3249
|
this.raise({ kind: "compaction", tone: "info", ...say("n_compaction_title", "n_compaction_body"), actions: [], ...sessionId2 === void 0 ? {} : { sessionId: sessionId2 } });
|
|
@@ -3366,11 +3285,13 @@ var Workspace = class {
|
|
|
3366
3285
|
break;
|
|
3367
3286
|
case "no-lanes": {
|
|
3368
3287
|
const offer = this.offers.find((item) => item.model === model2) ?? this.offers.find((item) => item.name.toLowerCase() === name2.toLowerCase());
|
|
3288
|
+
const body = failure.serverCode === "rental_stopped_unused" ? "n_laneStopped_body" : failure.planRenewsAt !== void 0 ? "n_planUsedUp_body" : failure.serverCode === "plan_usage_exhausted" ? "n_planUsedUpNoDate_body" : "n_noLanes_body";
|
|
3289
|
+
const until = failure.planRenewsAt === void 0 ? "" : `${new Date(failure.planRenewsAt * 1e3).toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
3369
3290
|
this.raise({
|
|
3370
3291
|
kind: "no-lanes",
|
|
3371
3292
|
tone: "warning",
|
|
3372
3293
|
modal: true,
|
|
3373
|
-
...say("n_noLanes_title",
|
|
3294
|
+
...say("n_noLanes_title", body, { until }),
|
|
3374
3295
|
actions: [button("get-access", "getAccess", { primary: true, ...offer === void 0 ? {} : { offerId: offer.id } }), button("dismiss", "notNow")]
|
|
3375
3296
|
});
|
|
3376
3297
|
break;
|
|
@@ -3380,7 +3301,7 @@ var Workspace = class {
|
|
|
3380
3301
|
kind: "out-of-credit",
|
|
3381
3302
|
tone: "error",
|
|
3382
3303
|
modal: true,
|
|
3383
|
-
...failure.minutesLeft === void 0 ? say("n_credit_title", "n_credit_body"
|
|
3304
|
+
...failure.minutesLeft === void 0 ? say("n_credit_title", "n_credit_body") : say("n_credit_title", "n_creditMinutes_body", { minutes: String(failure.minutesLeft) }),
|
|
3384
3305
|
actions: [button("add-funds", "n_addFunds", { url: this.links.models, primary: true }), button("dismiss", "notNow")]
|
|
3385
3306
|
});
|
|
3386
3307
|
break;
|
|
@@ -3560,7 +3481,7 @@ var Workspace = class {
|
|
|
3560
3481
|
if (target === void 0)
|
|
3561
3482
|
throw new Error("Choose an AdaptiqAI model first.");
|
|
3562
3483
|
const modelName = this.modelName(target);
|
|
3563
|
-
const
|
|
3484
|
+
const label2 = modelName ?? target;
|
|
3564
3485
|
let body;
|
|
3565
3486
|
try {
|
|
3566
3487
|
body = await this.deps.api.json(`/v1/memories/${encodeURIComponent(id)}/copies`, {
|
|
@@ -3576,7 +3497,7 @@ var Workspace = class {
|
|
|
3576
3497
|
this.copies.set(copyKey(id, target), { source: id, from, target, copy: null, status: "building" });
|
|
3577
3498
|
return { status: "building", model: target, modelName };
|
|
3578
3499
|
}
|
|
3579
|
-
throw new Error(copyFailure(error,
|
|
3500
|
+
throw new Error(copyFailure(error, label2));
|
|
3580
3501
|
}
|
|
3581
3502
|
const copy = obj4(body?.copy);
|
|
3582
3503
|
const status = copyStatus(str4(copy?.status));
|
|
@@ -4543,6 +4464,105 @@ var Workspace = class {
|
|
|
4543
4464
|
}
|
|
4544
4465
|
};
|
|
4545
4466
|
|
|
4467
|
+
// ../core/lib/tools.js
|
|
4468
|
+
var SOURCE_LIMIT = 6 * 1024 * 1024;
|
|
4469
|
+
|
|
4470
|
+
// ../core/lib/usage.js
|
|
4471
|
+
async function* parseSse(body, activity) {
|
|
4472
|
+
const decoder = new TextDecoder();
|
|
4473
|
+
const reader = body.getReader();
|
|
4474
|
+
let buffer = "";
|
|
4475
|
+
try {
|
|
4476
|
+
while (true) {
|
|
4477
|
+
const { value, done } = await reader.read();
|
|
4478
|
+
if (done)
|
|
4479
|
+
break;
|
|
4480
|
+
activity();
|
|
4481
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4482
|
+
let boundary = nextBoundary(buffer);
|
|
4483
|
+
while (boundary !== null) {
|
|
4484
|
+
const block = buffer.slice(0, boundary.index);
|
|
4485
|
+
buffer = buffer.slice(boundary.index + boundary.length);
|
|
4486
|
+
const event = parseBlock(block);
|
|
4487
|
+
if (event !== null)
|
|
4488
|
+
yield event;
|
|
4489
|
+
boundary = nextBoundary(buffer);
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
buffer += decoder.decode();
|
|
4493
|
+
const tail = parseBlock(buffer);
|
|
4494
|
+
if (tail !== null)
|
|
4495
|
+
yield tail;
|
|
4496
|
+
} finally {
|
|
4497
|
+
reader.releaseLock();
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
function nextBoundary(buffer) {
|
|
4501
|
+
const match = /\r?\n\r?\n/u.exec(buffer);
|
|
4502
|
+
return match === null ? null : { index: match.index, length: match[0].length };
|
|
4503
|
+
}
|
|
4504
|
+
function parseBlock(block) {
|
|
4505
|
+
let event = null;
|
|
4506
|
+
const data = [];
|
|
4507
|
+
for (const line of block.split(/\r?\n/u)) {
|
|
4508
|
+
const notice = /^: ?adaptiq-memory (.*)$/u.exec(line);
|
|
4509
|
+
if (notice !== null) {
|
|
4510
|
+
event = "adaptiq.memory";
|
|
4511
|
+
data.push(notice[1]);
|
|
4512
|
+
continue;
|
|
4513
|
+
}
|
|
4514
|
+
if (line === "" || line.startsWith(":"))
|
|
4515
|
+
continue;
|
|
4516
|
+
const colon = line.indexOf(":");
|
|
4517
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
4518
|
+
const value = colon === -1 ? "" : line.slice(colon + 1).replace(/^ /u, "");
|
|
4519
|
+
if (field === "event")
|
|
4520
|
+
event = value;
|
|
4521
|
+
else if (field === "data")
|
|
4522
|
+
data.push(value);
|
|
4523
|
+
}
|
|
4524
|
+
return data.length === 0 ? null : { event, data: data.join("\n") };
|
|
4525
|
+
}
|
|
4526
|
+
function mapUsage(raw) {
|
|
4527
|
+
const n = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
|
|
4528
|
+
const details = typeof raw.prompt_tokens_details === "object" && raw.prompt_tokens_details !== null ? raw.prompt_tokens_details : {};
|
|
4529
|
+
const completion = typeof raw.completion_tokens_details === "object" && raw.completion_tokens_details !== null ? raw.completion_tokens_details : {};
|
|
4530
|
+
const prompt = n(raw.prompt_tokens);
|
|
4531
|
+
const output = n(raw.completion_tokens);
|
|
4532
|
+
const cached = Math.min(prompt, n(details.cached_tokens) || n(raw.prompt_cache_hit_tokens));
|
|
4533
|
+
const written = Math.min(prompt - cached, n(details.cache_write_tokens) || n(raw.cache_write_tokens) || n(raw.cache_creation_input_tokens));
|
|
4534
|
+
const reasoning2 = n(completion.reasoning_tokens);
|
|
4535
|
+
const total = n(raw.total_tokens);
|
|
4536
|
+
return {
|
|
4537
|
+
inputTokens: Math.max(0, prompt - cached - written),
|
|
4538
|
+
outputTokens: output,
|
|
4539
|
+
...cached > 0 ? { cacheReadTokens: cached } : {},
|
|
4540
|
+
...written > 0 ? { cacheWriteTokens: written } : {},
|
|
4541
|
+
...reasoning2 > 0 ? { reasoningTokens: reasoning2 } : {},
|
|
4542
|
+
...total > 0 ? { totalTokens: total } : prompt + output > 0 ? { totalTokens: prompt + output } : {}
|
|
4543
|
+
};
|
|
4544
|
+
}
|
|
4545
|
+
function memoryNotice(raw) {
|
|
4546
|
+
let value = raw;
|
|
4547
|
+
if (typeof raw === "string") {
|
|
4548
|
+
try {
|
|
4549
|
+
value = JSON.parse(raw);
|
|
4550
|
+
} catch {
|
|
4551
|
+
return null;
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
if (typeof value !== "object" || value === null)
|
|
4555
|
+
return null;
|
|
4556
|
+
const notice = value;
|
|
4557
|
+
if (notice.kind !== "hibernated" && notice.kind !== "suggestion")
|
|
4558
|
+
return null;
|
|
4559
|
+
const sessions = Array.isArray(notice.sessions) ? notice.sessions.length : 0;
|
|
4560
|
+
if (sessions === 0)
|
|
4561
|
+
return null;
|
|
4562
|
+
const number = (field) => typeof field === "number" && Number.isFinite(field) && field >= 0 ? field : null;
|
|
4563
|
+
return { kind: notice.kind, sessions, tokens: number(notice.tokens), usageRatio: number(notice.usage_ratio) };
|
|
4564
|
+
}
|
|
4565
|
+
|
|
4546
4566
|
// src/host/stream.ts
|
|
4547
4567
|
var REASONING_FIELDS = ["reasoning_content", "reasoning", "reasoning_text"];
|
|
4548
4568
|
async function* translate(events, hooks) {
|
|
@@ -4671,7 +4691,70 @@ async function* translate(events, hooks) {
|
|
|
4671
4691
|
};
|
|
4672
4692
|
}
|
|
4673
4693
|
|
|
4694
|
+
// src/host/images.ts
|
|
4695
|
+
var ENGINE_IMAGE_LIMITS = Object.freeze({
|
|
4696
|
+
maxImages: 8,
|
|
4697
|
+
maxImageBytes: 20 * 1024 * 1024,
|
|
4698
|
+
maxPixels: 16e6
|
|
4699
|
+
});
|
|
4700
|
+
var ImageProblem = class extends Error {
|
|
4701
|
+
name = "ImageProblem";
|
|
4702
|
+
};
|
|
4703
|
+
var startsWith = (data, bytes, at = 0) => data.length >= at + bytes.length && bytes.every((byte, index) => data[at + index] === byte);
|
|
4704
|
+
var ascii = (text2) => [...text2].map((char) => char.charCodeAt(0));
|
|
4705
|
+
function sniffImageType(data) {
|
|
4706
|
+
if (startsWith(data, [137, 80, 78, 71, 13, 10, 26, 10])) return "image/png";
|
|
4707
|
+
if (startsWith(data, [255, 216, 255])) return "image/jpeg";
|
|
4708
|
+
if (startsWith(data, ascii("GIF87a")) || startsWith(data, ascii("GIF89a"))) return "image/gif";
|
|
4709
|
+
if (startsWith(data, ascii("RIFF")) && startsWith(data, ascii("WEBP"), 8)) return "image/webp";
|
|
4710
|
+
return null;
|
|
4711
|
+
}
|
|
4712
|
+
function fitPixels(width, height, maxPixels) {
|
|
4713
|
+
if (width * height <= maxPixels) return { width, height };
|
|
4714
|
+
const scale = Math.sqrt(maxPixels / (width * height));
|
|
4715
|
+
let w = Math.max(1, Math.floor(width * scale));
|
|
4716
|
+
let h = Math.max(1, Math.floor(height * scale));
|
|
4717
|
+
while (w * h > maxPixels) {
|
|
4718
|
+
if (w >= h) w--;
|
|
4719
|
+
else h--;
|
|
4720
|
+
}
|
|
4721
|
+
return { width: w, height: h };
|
|
4722
|
+
}
|
|
4723
|
+
var carriesImages = (message) => message.role === "user" || message.role === "tool";
|
|
4724
|
+
function sentImages(messages) {
|
|
4725
|
+
const out = [];
|
|
4726
|
+
for (const message of messages) {
|
|
4727
|
+
if (!carriesImages(message)) continue;
|
|
4728
|
+
for (const block of message.content) if (block.type === "image" && block.offloaded !== true) out.push(block);
|
|
4729
|
+
}
|
|
4730
|
+
return out;
|
|
4731
|
+
}
|
|
4732
|
+
function hasImages(messages) {
|
|
4733
|
+
return messages.some((message) => message.content.some((block) => block.type === "image"));
|
|
4734
|
+
}
|
|
4735
|
+
var mib = (bytes) => `${Math.round(bytes / (1024 * 1024) * 10) / 10} MB`;
|
|
4736
|
+
var label = (ref) => ref.name === void 0 ? "An image" : `The image "${ref.name}"`;
|
|
4737
|
+
async function prepareImages(messages, source, limits, signal) {
|
|
4738
|
+
const refs = /* @__PURE__ */ new Map();
|
|
4739
|
+
for (const block of sentImages(messages)) refs.set(String(block.attachment.attachmentId), block.attachment);
|
|
4740
|
+
const entries = await Promise.all([...refs].map(async ([id, ref]) => {
|
|
4741
|
+
const target = { ...fitPixels(ref.width, ref.height, limits.maxPixels), maxBytes: limits.maxImageBytes };
|
|
4742
|
+
const version = await source.readImageRequest(ref, target, signal);
|
|
4743
|
+
const mediaType = sniffImageType(version.data);
|
|
4744
|
+
if (mediaType === null) throw new ImageProblem(`${label(ref)} is not a PNG, JPEG, WebP or GIF file, so AdaptiqAI can't read it. Attach it in one of those formats.`);
|
|
4745
|
+
if (version.data.byteLength > limits.maxImageBytes) {
|
|
4746
|
+
throw new ImageProblem(`${label(ref)} is ${mib(version.data.byteLength)}; AdaptiqAI takes images up to ${mib(limits.maxImageBytes)}. Attach a smaller copy.`);
|
|
4747
|
+
}
|
|
4748
|
+
if (version.width * version.height > limits.maxPixels) {
|
|
4749
|
+
throw new ImageProblem(`${label(ref)} is ${version.width}\xD7${version.height} pixels, more than AdaptiqAI takes. Attach a smaller copy.`);
|
|
4750
|
+
}
|
|
4751
|
+
return [id, { url: `data:${mediaType};base64,${Buffer.from(version.data).toString("base64")}`, width: version.width, height: version.height }];
|
|
4752
|
+
}));
|
|
4753
|
+
return new Map(entries);
|
|
4754
|
+
}
|
|
4755
|
+
|
|
4674
4756
|
// src/host/wire.ts
|
|
4757
|
+
import { offloadedImageText, requestImageHandleText, textOnlyImageText } from "@deepseek-ai/dsh-llm";
|
|
4675
4758
|
function sessionReferences(message) {
|
|
4676
4759
|
if (message.role !== "user") return null;
|
|
4677
4760
|
const source = message.source;
|
|
@@ -4763,16 +4846,62 @@ function userTurns(messages) {
|
|
|
4763
4846
|
}
|
|
4764
4847
|
var text = (blocks) => blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
4765
4848
|
var reasoning = (blocks) => blocks.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
|
|
4849
|
+
var Parts = class {
|
|
4850
|
+
parts = [];
|
|
4851
|
+
glue = false;
|
|
4852
|
+
text(value, own = false) {
|
|
4853
|
+
if (value === "") return;
|
|
4854
|
+
const last = this.parts.at(-1);
|
|
4855
|
+
if (last?.type === "text") last.text += (own || this.glue ? "\n" : "") + value;
|
|
4856
|
+
else this.parts.push({ type: "text", text: value });
|
|
4857
|
+
this.glue = own;
|
|
4858
|
+
}
|
|
4859
|
+
image(url) {
|
|
4860
|
+
this.parts.push({ type: "image_url", image_url: { url } });
|
|
4861
|
+
this.glue = false;
|
|
4862
|
+
}
|
|
4863
|
+
get hasImage() {
|
|
4864
|
+
return this.parts.some((part) => part.type === "image_url");
|
|
4865
|
+
}
|
|
4866
|
+
get joined() {
|
|
4867
|
+
return this.parts.map((part) => part.type === "text" ? part.text : "").join("");
|
|
4868
|
+
}
|
|
4869
|
+
};
|
|
4870
|
+
function putImage(block, images, parts, inline) {
|
|
4871
|
+
const access2 = images?.access?.(block.attachment);
|
|
4872
|
+
if (block.offloaded === true) {
|
|
4873
|
+
parts.text(offloadedImageText(block.attachment, access2), true);
|
|
4874
|
+
return null;
|
|
4875
|
+
}
|
|
4876
|
+
const sent = images?.sent?.get(String(block.attachment.attachmentId));
|
|
4877
|
+
if (sent === void 0) {
|
|
4878
|
+
parts.text(textOnlyImageText(block.attachment), true);
|
|
4879
|
+
return null;
|
|
4880
|
+
}
|
|
4881
|
+
parts.text(requestImageHandleText(block.attachment, sent, access2), true);
|
|
4882
|
+
if (!inline) return sent;
|
|
4883
|
+
parts.image(sent.url);
|
|
4884
|
+
return null;
|
|
4885
|
+
}
|
|
4766
4886
|
function replayField(message) {
|
|
4767
4887
|
const source = message.source;
|
|
4768
4888
|
const state2 = source?.replayState;
|
|
4769
4889
|
const field = state2?.response?.reasoningField;
|
|
4770
4890
|
return typeof field === "string" && /^[a-z_]{1,32}$/u.test(field) ? field : "reasoning_content";
|
|
4771
4891
|
}
|
|
4772
|
-
function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
|
|
4892
|
+
function wireMessages(options, recalled = /* @__PURE__ */ new Map(), images) {
|
|
4773
4893
|
const out = [];
|
|
4774
4894
|
if (options.system !== void 0 && options.system !== "") out.push({ role: "system", content: options.system });
|
|
4895
|
+
let toolImages = [];
|
|
4896
|
+
const flushToolImages = () => {
|
|
4897
|
+
if (toolImages.length === 0) return;
|
|
4898
|
+
const parts = [{ type: "text", text: toolImages.length === 1 ? "The image from the tool result above:" : "The images from the tool results above, in order:" }];
|
|
4899
|
+
for (const image of toolImages) parts.push({ type: "image_url", image_url: { url: image.url } });
|
|
4900
|
+
out.push({ role: "user", content: parts });
|
|
4901
|
+
toolImages = [];
|
|
4902
|
+
};
|
|
4775
4903
|
options.messages.forEach((message, index) => {
|
|
4904
|
+
if (message.role !== "tool") flushToolImages();
|
|
4776
4905
|
switch (message.role) {
|
|
4777
4906
|
case "system": {
|
|
4778
4907
|
const content = text(message.content);
|
|
@@ -4784,6 +4913,15 @@ function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
|
|
|
4784
4913
|
return;
|
|
4785
4914
|
}
|
|
4786
4915
|
case "user": {
|
|
4916
|
+
if (message.content.some((block) => block.type === "image")) {
|
|
4917
|
+
const parts = new Parts();
|
|
4918
|
+
for (const block of message.content) {
|
|
4919
|
+
if (block.type === "text") parts.text(block.text);
|
|
4920
|
+
else if (block.type === "image") putImage(block, images, parts, true);
|
|
4921
|
+
}
|
|
4922
|
+
out.push({ role: "user", content: parts.hasImage ? parts.parts : parts.joined });
|
|
4923
|
+
return;
|
|
4924
|
+
}
|
|
4787
4925
|
const references = recalled.size === 0 ? null : sessionReferences(message);
|
|
4788
4926
|
const content = text(message.content);
|
|
4789
4927
|
out.push({ role: "user", content: references === null ? content : recallSnapshot(content, references, recalled) });
|
|
@@ -4805,7 +4943,15 @@ function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
|
|
|
4805
4943
|
return;
|
|
4806
4944
|
}
|
|
4807
4945
|
case "tool": {
|
|
4808
|
-
const
|
|
4946
|
+
const parts = new Parts();
|
|
4947
|
+
for (const block of message.content) {
|
|
4948
|
+
if (block.type === "text") parts.text(block.text);
|
|
4949
|
+
else if (block.type === "image") {
|
|
4950
|
+
const deferred = putImage(block, images, parts, false);
|
|
4951
|
+
if (deferred !== null) toolImages.push(deferred);
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
const content = parts.joined;
|
|
4809
4955
|
out.push({ role: "tool", tool_call_id: message.toolCallId, content: content === "" ? "(no output)" : content });
|
|
4810
4956
|
return;
|
|
4811
4957
|
}
|
|
@@ -4813,12 +4959,13 @@ function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
|
|
|
4813
4959
|
return;
|
|
4814
4960
|
}
|
|
4815
4961
|
});
|
|
4962
|
+
flushToolImages();
|
|
4816
4963
|
return out;
|
|
4817
4964
|
}
|
|
4818
4965
|
function buildChatBody(options, model2, extras) {
|
|
4819
4966
|
const body = {
|
|
4820
4967
|
model: options.model,
|
|
4821
|
-
messages: wireMessages(options, extras.recalled),
|
|
4968
|
+
messages: wireMessages(options, extras.recalled, extras.images),
|
|
4822
4969
|
stream: true,
|
|
4823
4970
|
stream_options: { include_usage: extras.includeUsage ?? true }
|
|
4824
4971
|
};
|
|
@@ -4910,7 +5057,6 @@ function resolvedInfo(route, model2) {
|
|
|
4910
5057
|
function describe(model2) {
|
|
4911
5058
|
const parts = [];
|
|
4912
5059
|
if (model2.nativeMemory || (model2.contextWindow ?? 0) >= 1e6) parts.push("Native memory");
|
|
4913
|
-
if (model2.lanes !== null) parts.push(`${model2.lanes} ${model2.lanes === 1 ? "lane" : "lanes"}`);
|
|
4914
5060
|
return parts.join(" \xB7 ");
|
|
4915
5061
|
}
|
|
4916
5062
|
function toLlmError(failure, cause) {
|
|
@@ -4970,6 +5116,41 @@ var AdaptiqAdapter = class extends LlmAdapter {
|
|
|
4970
5116
|
stream(options) {
|
|
4971
5117
|
return this.generate(options);
|
|
4972
5118
|
}
|
|
5119
|
+
/**
|
|
5120
|
+
* The request's images, read from the harness's stored copies, when the
|
|
5121
|
+
* model's card says it reads images. Otherwise each image goes as a short
|
|
5122
|
+
* note and the person is told.
|
|
5123
|
+
*/
|
|
5124
|
+
async images(options, model2, sessionId2, fail2, signal) {
|
|
5125
|
+
if (!hasImages(options.messages)) return void 0;
|
|
5126
|
+
const access2 = this.deps.imageAccess === void 0 ? void 0 : (ref) => {
|
|
5127
|
+
try {
|
|
5128
|
+
return this.deps.imageAccess?.(ref);
|
|
5129
|
+
} catch {
|
|
5130
|
+
return void 0;
|
|
5131
|
+
}
|
|
5132
|
+
};
|
|
5133
|
+
const withAccess = access2 === void 0 ? {} : { access: access2 };
|
|
5134
|
+
const problem = (message) => fail2({ code: CODES.invalidRequest, message, status: null, requestId: null, retryAfterMs: null, serverType: null, serverCode: null, notice: null });
|
|
5135
|
+
if (model2?.input.includes("image") !== true) {
|
|
5136
|
+
if (sentImages(options.messages).length > 0) this.deps.events.imagesNotSent?.(sessionId2, model2?.name ?? options.model);
|
|
5137
|
+
return { sent: null, ...withAccess };
|
|
5138
|
+
}
|
|
5139
|
+
const limits = this.deps.imageLimits ?? ENGINE_IMAGE_LIMITS;
|
|
5140
|
+
const excess = sentImages(options.messages).length - limits.maxImages;
|
|
5141
|
+
if (excess > 0) {
|
|
5142
|
+
throw new LlmError(`AdaptiqAI takes up to ${limits.maxImages} images per request; the ${excess} oldest ${excess === 1 ? "one goes" : "go"} as a note naming ${excess === 1 ? "it" : "them"}.`, IMAGE_OFFLOAD_REQUIRED_CODE, { offloadImages: excess });
|
|
5143
|
+
}
|
|
5144
|
+
const source = this.deps.attachments?.();
|
|
5145
|
+
if (source === void 0) throw problem("This DeepSeek Harness has no attachment service, so images can't be sent. Update DeepSeek Harness and try again.");
|
|
5146
|
+
try {
|
|
5147
|
+
return { sent: await prepareImages(options.messages, source, limits, signal), ...withAccess };
|
|
5148
|
+
} catch (error) {
|
|
5149
|
+
if (error instanceof ImageProblem) throw problem(error.message);
|
|
5150
|
+
if (signal.aborted) throw new LlmError("Request stopped.", CODES.aborted, { cause: error });
|
|
5151
|
+
throw problem(`An attached image could not be read: ${error instanceof Error ? error.message : String(error)}. Attach it again.`);
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
4973
5154
|
async *generate(options) {
|
|
4974
5155
|
if (options.purpose === "compaction") {
|
|
4975
5156
|
const message = "AdaptiqAI keeps the whole conversation in native memory, so it never needs compacting. Nothing was removed.";
|
|
@@ -5044,7 +5225,8 @@ var AdaptiqAdapter = class extends LlmAdapter {
|
|
|
5044
5225
|
if (references.length > 0) recalled = await this.deps.recall(sessionId2, options.model, references, signal).catch(() => NOTHING_RECALLED);
|
|
5045
5226
|
}
|
|
5046
5227
|
const parent = options.sessionId === void 0 ? void 0 : this.deps.parentSession?.(String(options.sessionId));
|
|
5047
|
-
|
|
5228
|
+
const images = await this.images(options, model2, sessionId2, fail2, signal);
|
|
5229
|
+
let body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled, images }));
|
|
5048
5230
|
this.deps.debug?.(`adaptiq: request ${sessionId2 ?? "-"}: ${options.messages.length} messages, ${body.length} bytes, ${recalled.size} read from memory, transcripts sent: ${body.includes("<referenced-sessions>") ? "yes" : "no"}`);
|
|
5049
5231
|
let response;
|
|
5050
5232
|
let refreshed = false;
|
|
@@ -5085,7 +5267,7 @@ var AdaptiqAdapter = class extends LlmAdapter {
|
|
|
5085
5267
|
await response.body?.cancel().catch(() => {
|
|
5086
5268
|
});
|
|
5087
5269
|
stored = false;
|
|
5088
|
-
body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled }));
|
|
5270
|
+
body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled, images }));
|
|
5089
5271
|
continue;
|
|
5090
5272
|
}
|
|
5091
5273
|
break;
|
|
@@ -5145,7 +5327,7 @@ var AdaptiqAdapter = class extends LlmAdapter {
|
|
|
5145
5327
|
|
|
5146
5328
|
// src/host/commands.ts
|
|
5147
5329
|
var HELP = [
|
|
5148
|
-
"/adaptiq \u2014 workspace, models
|
|
5330
|
+
"/adaptiq \u2014 workspace, models and memory",
|
|
5149
5331
|
"/adaptiq signin \u2014 connect your AdaptiqAI account in the browser",
|
|
5150
5332
|
"/adaptiq private \u2014 stop saving new messages of this conversation",
|
|
5151
5333
|
"/adaptiq remember \u2014 save this conversation to memory again",
|
|
@@ -5251,10 +5433,9 @@ ${HELP}`;
|
|
|
5251
5433
|
lines.push("No models yet. Pick one in Settings \u2192 AdaptiqAI.");
|
|
5252
5434
|
} else {
|
|
5253
5435
|
for (const model2 of snapshot.models) {
|
|
5254
|
-
const lanes = model2.lanes === null ? "" : ` \xB7 ${model2.lanes} ${model2.lanes === 1 ? "lane" : "lanes"}`;
|
|
5255
5436
|
const live = model2.runningNow === null ? "" : ` (${model2.runningNow} running, ${model2.waitingNow ?? 0} waiting)`;
|
|
5256
5437
|
const window = model2.contextWindow === null ? "" : ` \xB7 ${formatTokens(model2.contextWindow)} context`;
|
|
5257
|
-
lines.push(`\u2022 ${model2.name}${window}${
|
|
5438
|
+
lines.push(`\u2022 ${model2.name}${window}${live}${model2.status === "offline" ? " \xB7 offline" : ""}`);
|
|
5258
5439
|
}
|
|
5259
5440
|
}
|
|
5260
5441
|
if (memory?.available === true && memory.conversationsTokens !== null) {
|
|
@@ -5588,7 +5769,7 @@ var render = (_args, value) => {
|
|
|
5588
5769
|
return [{ type: "text", text: typeof text2 === "string" ? text2 : JSON.stringify(value) }];
|
|
5589
5770
|
};
|
|
5590
5771
|
var say2 = (result) => ({ result });
|
|
5591
|
-
var
|
|
5772
|
+
var SOURCE_LIMIT2 = 6 * 1024 * 1024;
|
|
5592
5773
|
var MEDIA = {
|
|
5593
5774
|
".pdf": "application/pdf",
|
|
5594
5775
|
".txt": "text/plain",
|
|
@@ -5845,7 +6026,7 @@ ${content}`);
|
|
|
5845
6026
|
const info = await stat(path);
|
|
5846
6027
|
if (!info.isFile()) fail(`${raw} is not a file.`);
|
|
5847
6028
|
bytes += info.size;
|
|
5848
|
-
if (bytes >
|
|
6029
|
+
if (bytes > SOURCE_LIMIT2) fail("These files are too large for one request. Use fewer or smaller files, or a repository link.");
|
|
5849
6030
|
const content = await readFile2(path);
|
|
5850
6031
|
files.push({ name: source.name ?? basename(path), mediaType: mediaTypeOf(path), contentBase64: content.toString("base64") });
|
|
5851
6032
|
} else {
|
|
@@ -6091,7 +6272,7 @@ Open **Settings \u2192 AdaptiqAI** to connect, or run \`/adaptiq signin\`.
|
|
|
6091
6272
|
var ENTRY_PROTOCOL = 1;
|
|
6092
6273
|
var RELOAD_SIGNATURE = true ? "ada3dfffa1b3d7b1" : "source";
|
|
6093
6274
|
function pluginVersion() {
|
|
6094
|
-
if (true) return "0.2.
|
|
6275
|
+
if (true) return "0.2.14";
|
|
6095
6276
|
try {
|
|
6096
6277
|
return createRequire(import.meta.url)("../package.json").version;
|
|
6097
6278
|
} catch {
|
|
@@ -6114,14 +6295,25 @@ async function harnessSessions(ctx) {
|
|
|
6114
6295
|
return new Map(records.map((record) => {
|
|
6115
6296
|
let title = null;
|
|
6116
6297
|
try {
|
|
6117
|
-
const
|
|
6118
|
-
if (typeof
|
|
6298
|
+
const label2 = typeof labels?.projectedLabels === "function" ? labels.projectedLabels(record).label : void 0;
|
|
6299
|
+
if (typeof label2 === "string" && label2.trim() !== "" && label2 !== record.header.id) title = label2.trim();
|
|
6119
6300
|
} catch {
|
|
6120
6301
|
}
|
|
6121
6302
|
const subagent = record.header.origin === "subagent" || (record.header.delegationDepth ?? 0) > 0;
|
|
6122
6303
|
return [record.header.id, { title, subagent }];
|
|
6123
6304
|
}));
|
|
6124
6305
|
}
|
|
6306
|
+
function attachmentService(ctx) {
|
|
6307
|
+
const service = ctx.get("attachments");
|
|
6308
|
+
return typeof service?.readImageRequest === "function" ? service : void 0;
|
|
6309
|
+
}
|
|
6310
|
+
function imageAccess(ctx, ref) {
|
|
6311
|
+
const host = attachmentService(ctx)?.imageHostPath?.(ref);
|
|
6312
|
+
if (host === void 0) return void 0;
|
|
6313
|
+
const fs = ctx.get("fs");
|
|
6314
|
+
const readonlyPath = fs?.processPathFromHostPath?.(host);
|
|
6315
|
+
return readonlyPath === void 0 ? void 0 : { readonlyPath };
|
|
6316
|
+
}
|
|
6125
6317
|
var VERSION = pluginVersion();
|
|
6126
6318
|
async function apply(ctx, config) {
|
|
6127
6319
|
await applyWith(ctx, config, void 0);
|
|
@@ -6146,8 +6338,8 @@ async function applyWith(ctx, raw, runtime) {
|
|
|
6146
6338
|
onChange: () => {
|
|
6147
6339
|
workspace?.authChanged();
|
|
6148
6340
|
},
|
|
6149
|
-
onSignedIn: (
|
|
6150
|
-
workspace?.signedIn(
|
|
6341
|
+
onSignedIn: (label2) => {
|
|
6342
|
+
workspace?.signedIn(label2);
|
|
6151
6343
|
}
|
|
6152
6344
|
});
|
|
6153
6345
|
const api = new AdaptiqApi(apiBase, auth, () => ({
|
|
@@ -6229,6 +6421,8 @@ async function applyWith(ctx, raw, runtime) {
|
|
|
6229
6421
|
attachPending: (sessionId2) => owned.attachPending(sessionId2),
|
|
6230
6422
|
events: owned,
|
|
6231
6423
|
autoSleep: (model2) => owned.autoSleep(model2),
|
|
6424
|
+
attachments: () => attachmentService(ctx),
|
|
6425
|
+
imageAccess: (ref) => imageAccess(ctx, ref),
|
|
6232
6426
|
...process.env.DSH_ADAPTIQ_DEBUG_LOG === void 0 || process.env.DSH_ADAPTIQ_DEBUG_LOG === "" ? {} : { debug: (message) => {
|
|
6233
6427
|
log("info", message);
|
|
6234
6428
|
} }
|