@huaweidevtools/aishell-dsh-llm 0.0.15 → 0.0.17
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/lib/index.js +147 -20
- package/lib/types/adapter.d.ts +50 -18
- package/lib/types/client/index.d.ts +3 -4
- package/lib/types/client/provider-row-shadow.d.ts +13 -15
- package/lib/types/images.d.ts +4 -3
- package/lib/types/index.d.ts +5 -5
- package/lib/types/serialize.d.ts +5 -5
- package/lib/types/translate.d.ts +6 -2
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createCredentialsStore } from "@huaweidevtools/aishell-dsh-credentials-store";
|
|
2
|
-
import { IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, contentHasImage, offloadedImageText, projectOffloadedImages, requestImageHandleText, requiredImageOffload, resolveImageAttachmentAccess } from "@deepseek-ai/dsh-llm";
|
|
2
|
+
import { IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, contentHasImage, offloadedImageText, projectOffloadedImages, requestImageHandleText, requiredImageOffload, resolveImageAttachmentAccess, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { requestImageDimensions } from "@deepseek-ai/dsh-attachment";
|
|
4
4
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
5
5
|
import { EventSourceParserStream } from "eventsource-parser/stream";
|
|
@@ -977,9 +977,10 @@ function requireRequestImage(images, ref) {
|
|
|
977
977
|
* Replace every offloaded occurrence with its placeholder text.
|
|
978
978
|
*
|
|
979
979
|
* The durable `offloaded` mark is a surface fact shared by all routes; only
|
|
980
|
-
* the placeholder wording is route-owned. Request assembly
|
|
981
|
-
*
|
|
982
|
-
*
|
|
980
|
+
* the placeholder wording is route-owned. Request assembly leaves this to the
|
|
981
|
+
* adapter, so skipping it would send an image whose bytes this route
|
|
982
|
+
* deliberately never loaded — an empty `image_url` rather than the text that
|
|
983
|
+
* explains where it went.
|
|
983
984
|
*
|
|
984
985
|
* @param messages - complete request history.
|
|
985
986
|
* @param resolveAccess - execution-world path resolver, when one is available.
|
|
@@ -1179,20 +1180,34 @@ function contentFor(blocks, images, resolveAccess) {
|
|
|
1179
1180
|
* target is for), so the version is the only description of the bytes actually
|
|
1180
1181
|
* being sent.
|
|
1181
1182
|
*
|
|
1182
|
-
* Encoded through `btoa` over
|
|
1183
|
-
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
*
|
|
1183
|
+
* Encoded through `btoa` over a byte string rather than `Buffer`: this package
|
|
1184
|
+
* compiles host and browser halves in one project under `types: []` with the
|
|
1185
|
+
* DOM lib, so the Node global is neither declared nor reachable here. `btoa` is
|
|
1186
|
+
* a global in both Node 22+ and every browser, which is why it is the one that
|
|
1187
|
+
* typechecks in both halves.
|
|
1188
|
+
*
|
|
1189
|
+
* The byte string is built in chunks. One `String.fromCharCode` per byte is
|
|
1190
|
+
* quadratic in the string's length on engines without rope concatenation, and
|
|
1191
|
+
* a request image may carry a megabyte; spreading a bounded chunk into the
|
|
1192
|
+
* call is linear and stays under the argument-count limit.
|
|
1187
1193
|
*
|
|
1188
1194
|
* @param version - prepared request version.
|
|
1189
1195
|
* @returns a base64 data URL.
|
|
1190
1196
|
*/
|
|
1191
1197
|
function dataUrl(version) {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1198
|
+
const bytes = version.data;
|
|
1199
|
+
const chunks = [];
|
|
1200
|
+
for (let start = 0; start < bytes.length; start += BYTE_STRING_CHUNK) chunks.push(String.fromCharCode(...bytes.subarray(start, start + BYTE_STRING_CHUNK)));
|
|
1201
|
+
return `data:${version.mediaType};base64,${btoa(chunks.join(""))}`;
|
|
1195
1202
|
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Bytes converted per `String.fromCharCode` call.
|
|
1205
|
+
*
|
|
1206
|
+
* A power of two below the argument-count ceiling every engine allows (the
|
|
1207
|
+
* lowest is 65535), chosen so the spread stays well clear of it rather than at
|
|
1208
|
+
* the edge.
|
|
1209
|
+
*/
|
|
1210
|
+
const BYTE_STRING_CHUNK = 32768;
|
|
1196
1211
|
/** Type guard for tool-call blocks. */
|
|
1197
1212
|
function isToolCallBlock(block) {
|
|
1198
1213
|
return block.type === "tool-call";
|
|
@@ -1390,6 +1405,7 @@ async function* translate(payloads) {
|
|
|
1390
1405
|
...reasoningBlock !== void 0 ? [reasoningBlock] : [],
|
|
1391
1406
|
...toolBlocks.values()
|
|
1392
1407
|
].sort((a, b) => a.index - b.index);
|
|
1408
|
+
if (allBlocks.length === 0 && (pendingFinish === void 0 || pendingFinish.kind === "stop")) throw new LlmError("aishell-llm: gateway returned a stream with no content", "EMPTY_RESPONSE");
|
|
1393
1409
|
for (const block of allBlocks) yield {
|
|
1394
1410
|
type: "block-end",
|
|
1395
1411
|
index: block.index,
|
|
@@ -1427,6 +1443,40 @@ const PROVIDER_DISPLAY_NAME = "AI Shell";
|
|
|
1427
1443
|
//#endregion
|
|
1428
1444
|
//#region src/adapter.ts
|
|
1429
1445
|
/**
|
|
1446
|
+
* Retries after the first attempt.
|
|
1447
|
+
*
|
|
1448
|
+
* Five, so a request is tried six times at most. Six requests is the point
|
|
1449
|
+
* where a rate limit that has not cleared is more likely to be a quota than a
|
|
1450
|
+
* burst, and the last delay alone is sixteen seconds.
|
|
1451
|
+
*/
|
|
1452
|
+
const RETRY_MAX_ATTEMPTS = 5;
|
|
1453
|
+
/**
|
|
1454
|
+
* First backoff delay. The ladder is this value doubled per attempt — 1, 2, 4,
|
|
1455
|
+
* 8, 16 seconds — so the cap below is what fixes the last rung.
|
|
1456
|
+
*/
|
|
1457
|
+
const RETRY_INITIAL_DELAY_MS = 1e3;
|
|
1458
|
+
/** Cap on one delay. Equal to the fifth rung, so the ladder is exact. */
|
|
1459
|
+
const RETRY_MAX_DELAY_MS = 16e3;
|
|
1460
|
+
/**
|
|
1461
|
+
* Failures worth another attempt.
|
|
1462
|
+
*
|
|
1463
|
+
* Transient by definition: a rate limit asks for a later attempt, a server
|
|
1464
|
+
* error and a timeout say nothing about the request, and `EMPTY_RESPONSE` is
|
|
1465
|
+
* documented as safe to repeat because the attempt left nothing durable.
|
|
1466
|
+
*
|
|
1467
|
+
* Absent on purpose: `PROVIDER_ERROR` (a 4xx that is not a rate limit is a
|
|
1468
|
+
* statement about the request — a bad model id, a malformed body — and
|
|
1469
|
+
* repeating it returns the same answer), and `MISSING_CREDENTIAL` /
|
|
1470
|
+
* `INVALID_CREDENTIAL` (a retry cannot conjure a credential).
|
|
1471
|
+
*/
|
|
1472
|
+
const RETRYABLE_CODES = [
|
|
1473
|
+
"RATE_LIMIT",
|
|
1474
|
+
"SERVER",
|
|
1475
|
+
"TIMEOUT",
|
|
1476
|
+
"TRANSPORT",
|
|
1477
|
+
"EMPTY_RESPONSE"
|
|
1478
|
+
];
|
|
1479
|
+
/**
|
|
1430
1480
|
* Whether a gateway error body reports an id the gateway does not carry.
|
|
1431
1481
|
*
|
|
1432
1482
|
* Both fields are accepted because the gateway emits them from one definition
|
|
@@ -1450,6 +1500,40 @@ function isModelNotFound(text) {
|
|
|
1450
1500
|
return typeof inner === "object" && inner !== null && inner.code === "model_not_found";
|
|
1451
1501
|
}
|
|
1452
1502
|
/**
|
|
1503
|
+
* Map an HTTP status onto the harness's retryable failure vocabulary.
|
|
1504
|
+
*
|
|
1505
|
+
* ## Why this matters
|
|
1506
|
+
*
|
|
1507
|
+
* `resolveRetryPolicy` decides whether to retry from the **error code**, not
|
|
1508
|
+
* from the status. An adapter that reports every non-2xx as one undifferentiated
|
|
1509
|
+
* code makes the retry policy blind: a 429 and a 401 would look identical, so
|
|
1510
|
+
* either every failure retries or none does.
|
|
1511
|
+
*
|
|
1512
|
+
* ## The mapping
|
|
1513
|
+
*
|
|
1514
|
+
* - `429` — `RATE_LIMIT`. The one case where the gateway is explicitly
|
|
1515
|
+
* asking for a later attempt.
|
|
1516
|
+
* - `5xx` — `SERVER`. A transient upstream fault; the same request may
|
|
1517
|
+
* succeed.
|
|
1518
|
+
* - `408` — `TIMEOUT`. The gateway timed out waiting; nothing about the
|
|
1519
|
+
* request is wrong.
|
|
1520
|
+
* - everything else — `PROVIDER_ERROR`. A 4xx that is not a rate limit is a
|
|
1521
|
+
* statement about the request itself (a bad model id, a malformed body, a
|
|
1522
|
+
* rejected credential), and repeating it produces the same answer.
|
|
1523
|
+
*
|
|
1524
|
+
* `PROVIDER_ERROR` is not in the default retryable set, which is exactly the
|
|
1525
|
+
* intent: refusing to retry a request the provider already said was wrong.
|
|
1526
|
+
*
|
|
1527
|
+
* @param status - the HTTP status of the rejected request.
|
|
1528
|
+
* @returns the code to carry on the `LlmError`.
|
|
1529
|
+
*/
|
|
1530
|
+
function classifyHttpFailure(status) {
|
|
1531
|
+
if (status === 429) return "RATE_LIMIT";
|
|
1532
|
+
if (status === 408) return "TIMEOUT";
|
|
1533
|
+
if (status >= 500 && status <= 599) return "SERVER";
|
|
1534
|
+
return "PROVIDER_ERROR";
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1453
1537
|
* Map the gateway's `modalities` array (e.g. `["text","image"]`) to DSH's
|
|
1454
1538
|
* `ModelModality` vocabulary. DSH currently defines only `'text' | 'image'`;
|
|
1455
1539
|
* other modalities (audio, video) are dropped — the gateway still accepts
|
|
@@ -1477,6 +1561,48 @@ var GatewayAdapter = class GatewayAdapter extends LlmAdapter {
|
|
|
1477
1561
|
};
|
|
1478
1562
|
}
|
|
1479
1563
|
/**
|
|
1564
|
+
* The retry policy this route runs under.
|
|
1565
|
+
*
|
|
1566
|
+
* ## Why the adapter owns it
|
|
1567
|
+
*
|
|
1568
|
+
* The executor (`@deepseek-ai/dsh-llm-retry`, mounted by `dsh-base`) retries
|
|
1569
|
+
* whatever a provider's policy says to retry, and it reads the policy from
|
|
1570
|
+
* the provider registration. Declaring it here is what lets a 429 on this
|
|
1571
|
+
* gateway be retried without retrying a 401 — the previous code reported
|
|
1572
|
+
* every non-2xx as `PROVIDER_ERROR`, which put nothing in the retryable set
|
|
1573
|
+
* and so retried nothing.
|
|
1574
|
+
*
|
|
1575
|
+
* ## The shape
|
|
1576
|
+
*
|
|
1577
|
+
* Five retries after the first attempt, so six requests at most. Delays are
|
|
1578
|
+
* 1, 2, 4, 8 and 16 seconds: `localDelay` computes
|
|
1579
|
+
* `initialDelayMs * 2 ** (retry - 1)` capped at `maxDelayMs`, so the caps and
|
|
1580
|
+
* the initial delay together spell that exact ladder.
|
|
1581
|
+
*
|
|
1582
|
+
* `jitterRatio: 0` is deliberate. Jitter exists to spread a fleet's retries
|
|
1583
|
+
* off one another; this adapter has one client per user, and the countdown
|
|
1584
|
+
* the chat UI renders is the scheduled delay — jitter would make it disagree
|
|
1585
|
+
* with the number the user is told.
|
|
1586
|
+
*
|
|
1587
|
+
* Eligible codes are the transient ones. `PROVIDER_ERROR` is deliberately
|
|
1588
|
+
* absent: a 4xx that is not a rate limit is a statement about the request,
|
|
1589
|
+
* and repeating it produces the same answer. `EMPTY_RESPONSE` is included
|
|
1590
|
+
* because the harness documents it as safe to repeat — the attempt produced
|
|
1591
|
+
* nothing durable.
|
|
1592
|
+
*/
|
|
1593
|
+
providerRetryPolicy(_provider) {
|
|
1594
|
+
return resolveRetryPolicy({
|
|
1595
|
+
mode: "normal",
|
|
1596
|
+
maxRetries: RETRY_MAX_ATTEMPTS,
|
|
1597
|
+
retryableCodes: [...RETRYABLE_CODES],
|
|
1598
|
+
backoff: {
|
|
1599
|
+
initialDelayMs: RETRY_INITIAL_DELAY_MS,
|
|
1600
|
+
maxDelayMs: RETRY_MAX_DELAY_MS,
|
|
1601
|
+
jitterRatio: 0
|
|
1602
|
+
}
|
|
1603
|
+
}, "aishell-llm: retry policy");
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1480
1606
|
* Fetch the raw wire models from `/models`, cached for a short TTL so
|
|
1481
1607
|
* `listModels` and `resolveModel` don't each sign a separate request.
|
|
1482
1608
|
* Returns `[]` on any failure (missing credentials, network, non-ok) so
|
|
@@ -1612,13 +1738,14 @@ var GatewayAdapter = class GatewayAdapter extends LlmAdapter {
|
|
|
1612
1738
|
* way.
|
|
1613
1739
|
*
|
|
1614
1740
|
* Deliberately the whole image pipeline and nothing else, so `stream` stays
|
|
1615
|
-
* about transport.
|
|
1616
|
-
* resolve the attachment service, prepare one request version per
|
|
1617
|
-
* image, fail the request
|
|
1741
|
+
* about transport. The ordering is load-bearing and matches the official
|
|
1742
|
+
* adapters: resolve the attachment service, prepare one request version per
|
|
1743
|
+
* retained image, fail the request when the budget is exceeded (handing
|
|
1618
1744
|
* `dsh-compaction-image-offload` its decision), and only then project the
|
|
1619
|
-
* offloaded occurrences to placeholder text
|
|
1620
|
-
*
|
|
1621
|
-
*
|
|
1745
|
+
* offloaded occurrences to placeholder text. That projection has to come
|
|
1746
|
+
* last: an offloaded occurrence is one an earlier request decided not to
|
|
1747
|
+
* send, so serialization replaces it with text rather than looking up bytes
|
|
1748
|
+
* that were deliberately never loaded.
|
|
1622
1749
|
*/
|
|
1623
1750
|
async buildBody(options) {
|
|
1624
1751
|
if (!historyHasImage(options.messages)) return JSON.stringify(serializeRequest(options, void 0, this.imageAccess));
|
|
@@ -1642,11 +1769,11 @@ var GatewayAdapter = class GatewayAdapter extends LlmAdapter {
|
|
|
1642
1769
|
} catch (error) {
|
|
1643
1770
|
if (options.signal?.aborted) throw error;
|
|
1644
1771
|
if (error instanceof LlmError) throw error;
|
|
1645
|
-
throw new LlmError(`aishell-llm: request failed: ${error instanceof Error ? error.message : String(error)}`, "
|
|
1772
|
+
throw new LlmError(`aishell-llm: request failed: ${error instanceof Error ? error.message : String(error)}`, "TRANSPORT");
|
|
1646
1773
|
}
|
|
1647
1774
|
if (!response.ok) {
|
|
1648
1775
|
const text = await response.text().catch(() => "");
|
|
1649
|
-
throw new LlmError(`aishell-llm: gateway returned ${response.status}: ${text}` + await this.unsupportedModelHint(text, options),
|
|
1776
|
+
throw new LlmError(`aishell-llm: gateway returned ${response.status}: ${text}` + await this.unsupportedModelHint(text, options), classifyHttpFailure(response.status), { status: response.status });
|
|
1650
1777
|
}
|
|
1651
1778
|
if (!response.body) throw new LlmError("aishell-llm: gateway returned no body", "EMPTY_RESPONSE");
|
|
1652
1779
|
yield* translate(parseSse(response.body));
|
package/lib/types/adapter.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* aishell LLM gateway adapter for DSH.
|
|
3
3
|
*
|
|
4
|
-
* Routes model calls through the aishell gateway using direct
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Routes model calls through the aishell gateway using direct AK/SK signing
|
|
5
|
+
* (SDK-HMAC-SHA256). Every request — chat and model listing alike — is signed
|
|
6
|
+
* with the user's AK/SK read from the credential store, so there is no API-key
|
|
7
|
+
* exchange and no separate config fetch between the credential and the call.
|
|
8
8
|
*
|
|
9
9
|
* @module @huaweidevtools/aishell-dsh-llm/adapter
|
|
10
10
|
*/
|
|
11
11
|
import { LlmAdapter } from '@deepseek-ai/dsh-llm';
|
|
12
|
-
import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
12
|
+
import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
13
13
|
import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
|
|
14
14
|
import type { CredentialsStore } from '@huaweidevtools/aishell-dsh-credentials-store';
|
|
15
15
|
import { type ImageRequestBudget } from './images.ts';
|
|
@@ -18,14 +18,14 @@ export interface GatewayAdapterOptions {
|
|
|
18
18
|
/**
|
|
19
19
|
* Resolve the current chat API base URL (e.g. `https://openai.aishell.openbuildlab.com/v1`).
|
|
20
20
|
*
|
|
21
|
-
* A reader, not a value,
|
|
22
|
-
*
|
|
23
|
-
* volatile-only update. The loader commits such an update into the existing
|
|
21
|
+
* A reader, not a value, because this plugin's whole Config is `Volatile`: a
|
|
22
|
+
* settings edit is a volatile-only update that commits into the existing
|
|
24
23
|
* references and returns without re-running `apply()`
|
|
25
|
-
* (`cordis-plugin-loader/lib/index.js:381-388`)
|
|
26
|
-
*
|
|
27
|
-
* leave the settings surface showing the new endpoint while requests
|
|
28
|
-
* going to the
|
|
24
|
+
* (`cordis-plugin-loader/lib/index.js:381-388`). The adapter instance
|
|
25
|
+
* therefore outlives the values it was built with, and a captured string
|
|
26
|
+
* would leave the settings surface showing the new endpoint while requests
|
|
27
|
+
* kept going to the endpoint from construction — silently, for the life of
|
|
28
|
+
* the process.
|
|
29
29
|
*/
|
|
30
30
|
readonly resolveBaseUrl: () => string;
|
|
31
31
|
/** Credential store for reading AK/SK. */
|
|
@@ -55,6 +55,37 @@ export declare class GatewayAdapter extends LlmAdapter {
|
|
|
55
55
|
private static readonly MODELS_TTL_MS;
|
|
56
56
|
constructor(opts: GatewayAdapterOptions);
|
|
57
57
|
providerInfo(provider: string): LlmProviderInfo;
|
|
58
|
+
/**
|
|
59
|
+
* The retry policy this route runs under.
|
|
60
|
+
*
|
|
61
|
+
* ## Why the adapter owns it
|
|
62
|
+
*
|
|
63
|
+
* The executor (`@deepseek-ai/dsh-llm-retry`, mounted by `dsh-base`) retries
|
|
64
|
+
* whatever a provider's policy says to retry, and it reads the policy from
|
|
65
|
+
* the provider registration. Declaring it here is what lets a 429 on this
|
|
66
|
+
* gateway be retried without retrying a 401 — the previous code reported
|
|
67
|
+
* every non-2xx as `PROVIDER_ERROR`, which put nothing in the retryable set
|
|
68
|
+
* and so retried nothing.
|
|
69
|
+
*
|
|
70
|
+
* ## The shape
|
|
71
|
+
*
|
|
72
|
+
* Five retries after the first attempt, so six requests at most. Delays are
|
|
73
|
+
* 1, 2, 4, 8 and 16 seconds: `localDelay` computes
|
|
74
|
+
* `initialDelayMs * 2 ** (retry - 1)` capped at `maxDelayMs`, so the caps and
|
|
75
|
+
* the initial delay together spell that exact ladder.
|
|
76
|
+
*
|
|
77
|
+
* `jitterRatio: 0` is deliberate. Jitter exists to spread a fleet's retries
|
|
78
|
+
* off one another; this adapter has one client per user, and the countdown
|
|
79
|
+
* the chat UI renders is the scheduled delay — jitter would make it disagree
|
|
80
|
+
* with the number the user is told.
|
|
81
|
+
*
|
|
82
|
+
* Eligible codes are the transient ones. `PROVIDER_ERROR` is deliberately
|
|
83
|
+
* absent: a 4xx that is not a rate limit is a statement about the request,
|
|
84
|
+
* and repeating it produces the same answer. `EMPTY_RESPONSE` is included
|
|
85
|
+
* because the harness documents it as safe to repeat — the attempt produced
|
|
86
|
+
* nothing durable.
|
|
87
|
+
*/
|
|
88
|
+
providerRetryPolicy(_provider: string): ResolvedRetryPolicy;
|
|
58
89
|
/**
|
|
59
90
|
* Fetch the raw wire models from `/models`, cached for a short TTL so
|
|
60
91
|
* `listModels` and `resolveModel` don't each sign a separate request.
|
|
@@ -130,13 +161,14 @@ export declare class GatewayAdapter extends LlmAdapter {
|
|
|
130
161
|
* way.
|
|
131
162
|
*
|
|
132
163
|
* Deliberately the whole image pipeline and nothing else, so `stream` stays
|
|
133
|
-
* about transport.
|
|
134
|
-
* resolve the attachment service, prepare one request version per
|
|
135
|
-
* image, fail the request
|
|
164
|
+
* about transport. The ordering is load-bearing and matches the official
|
|
165
|
+
* adapters: resolve the attachment service, prepare one request version per
|
|
166
|
+
* retained image, fail the request when the budget is exceeded (handing
|
|
136
167
|
* `dsh-compaction-image-offload` its decision), and only then project the
|
|
137
|
-
* offloaded occurrences to placeholder text
|
|
138
|
-
*
|
|
139
|
-
*
|
|
168
|
+
* offloaded occurrences to placeholder text. That projection has to come
|
|
169
|
+
* last: an offloaded occurrence is one an earlier request decided not to
|
|
170
|
+
* send, so serialization replaces it with text rather than looking up bytes
|
|
171
|
+
* that were deliberately never loaded.
|
|
140
172
|
*/
|
|
141
173
|
private buildBody;
|
|
142
174
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The page's row for the aishell family is read-only: its profile has no
|
|
5
5
|
* fields the host editor knows how to write, and its credentials are entered
|
|
6
|
-
* through the credential form the `aishell-credentials` chips open.
|
|
7
|
-
* row should
|
|
8
|
-
*
|
|
9
|
-
* row's name and hides the header's Edit button (see
|
|
6
|
+
* through the credential form the `aishell-credentials` chips open. The one
|
|
7
|
+
* fact the row should carry is readiness, so the browser half paints it as a
|
|
8
|
+
* status dot beside the row's name and hides the header's Edit button (see
|
|
10
9
|
* `client/provider-row-shadow.ts`).
|
|
11
10
|
*
|
|
12
11
|
* The readiness fact is read from the `aishell-client-credentials` settings
|
|
@@ -2,23 +2,21 @@
|
|
|
2
2
|
* Runtime shadow of the aishell provider's Models-page row.
|
|
3
3
|
*
|
|
4
4
|
* The Models page renders each configured provider as a row whose header
|
|
5
|
-
* (`rowHead`) shows the display name followed by an Edit button
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* in a card below the header.
|
|
5
|
+
* (`rowHead`) shows the display name followed by an Edit button this family
|
|
6
|
+
* has no use for: the profile has no fields the host editor knows how to
|
|
7
|
+
* write, and the row's directory entry names no api-key credential, so the
|
|
8
|
+
* host header paints no status dot either. What the row should carry is the
|
|
9
|
+
* one fact that matters — ready or not — and it reads better beside the name
|
|
10
|
+
* than in a card below the header.
|
|
12
11
|
*
|
|
13
12
|
* The header lives in ui-settings-models, digest-protected with no slot for
|
|
14
|
-
* the identity half (the `settings.models.provider-card` seat extends the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* patch already relies on.
|
|
13
|
+
* the identity half (the `settings.models.provider-card` seat extends the area
|
|
14
|
+
* BELOW the header, which is exactly the part this family does not want). The
|
|
15
|
+
* shadow therefore works the way aishell-brand's host residues do: find the
|
|
16
|
+
* row, patch the DOM. The row is located by its rendered name (the directory
|
|
17
|
+
* entry's displayName, `PROVIDER_DISPLAY_NAME` — a literal both halves share);
|
|
18
|
+
* the class names beside it are CSS-module hashes, so they are matched by the
|
|
19
|
+
* same `[class*="…"]` substring selectors the hero patch already relies on.
|
|
22
20
|
*
|
|
23
21
|
* What the shadow does to a found row:
|
|
24
22
|
*
|
package/lib/types/images.d.ts
CHANGED
|
@@ -94,9 +94,10 @@ export declare function requireRequestImage(images: RequestImages, ref: ImageAtt
|
|
|
94
94
|
* Replace every offloaded occurrence with its placeholder text.
|
|
95
95
|
*
|
|
96
96
|
* The durable `offloaded` mark is a surface fact shared by all routes; only
|
|
97
|
-
* the placeholder wording is route-owned. Request assembly
|
|
98
|
-
*
|
|
99
|
-
*
|
|
97
|
+
* the placeholder wording is route-owned. Request assembly leaves this to the
|
|
98
|
+
* adapter, so skipping it would send an image whose bytes this route
|
|
99
|
+
* deliberately never loaded — an empty `image_url` rather than the text that
|
|
100
|
+
* explains where it went.
|
|
100
101
|
*
|
|
101
102
|
* @param messages - complete request history.
|
|
102
103
|
* @param resolveAccess - execution-world path resolver, when one is available.
|
package/lib/types/index.d.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Registers the `aishell` provider route on `ctx.llm`, routing model calls
|
|
5
5
|
* through the aishell gateway. Every request is signed directly with the
|
|
6
|
-
* user's AK/SK (SDK-HMAC-SHA256)
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* user's AK/SK (SDK-HMAC-SHA256), read from the credential store (OS keyring
|
|
7
|
+
* with memory fallback) via `createCredentialsStore` — there is no API-key
|
|
8
|
+
* exchange between the credential and the call.
|
|
9
9
|
*
|
|
10
10
|
* Credential management (settings namespace + watch → store transfer) lives
|
|
11
11
|
* in `aishell-credentials`; this plugin only consumes the stored credentials.
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* directory (`registerConfigurableProviders`), which is what the Models page
|
|
15
15
|
* joins with live routes — and it carries a Config schema, without which the
|
|
16
16
|
* settings mirror (and therefore the Models row) never lists this namespace.
|
|
17
|
-
* The row
|
|
18
|
-
*
|
|
17
|
+
* The row has no card of its own: the browser half paints a status dot beside
|
|
18
|
+
* the row's name and hides the Edit button, which has nothing to edit (see
|
|
19
19
|
* `client/provider-row-shadow.ts`).
|
|
20
20
|
*
|
|
21
21
|
* @module @huaweidevtools/aishell-dsh-llm
|
package/lib/types/serialize.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Serialize harness {@link GenerateOptions} into an OpenAI-compatible wire request.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* list instead
|
|
7
|
-
* a base64 data URL
|
|
8
|
-
*
|
|
4
|
+
* A message without an image serializes to a plain string — the shape every
|
|
5
|
+
* OpenAI-compatible endpoint accepts for text. A message carrying one
|
|
6
|
+
* serializes to an ordered part list instead: a handle-text part naming the
|
|
7
|
+
* image, then the image itself as a base64 data URL, which is the only shape
|
|
8
|
+
* those endpoints accept image input in.
|
|
9
9
|
*
|
|
10
10
|
* Image bytes are not read here. The harness hands over durable references,
|
|
11
11
|
* not bytes, so `prepareRequestImages` resolves them one step ahead of this
|
package/lib/types/translate.d.ts
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
* Translate OpenAI-compatible SSE payloads into harness {@link StreamChunk}s.
|
|
3
3
|
*
|
|
4
4
|
* One stateful block per content, reasoning, or tool-call index. Finish reason
|
|
5
|
-
* and usage are deferred until `[DONE]`,
|
|
6
|
-
*
|
|
5
|
+
* and usage are deferred until `[DONE]`, which covers both shapes a
|
|
6
|
+
* compatible endpoint produces: usage attached to the finish chunk, and usage
|
|
7
|
+
* in a trailing chunk of its own.
|
|
8
|
+
*
|
|
9
|
+
* A stream that settles without producing any block is a failure rather than
|
|
10
|
+
* an empty turn — see the note on the check before the blocks close.
|
|
7
11
|
*
|
|
8
12
|
* @module @huaweidevtools/aishell-dsh-llm/translate
|
|
9
13
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huaweidevtools/aishell-dsh-llm",
|
|
3
3
|
"description": "aishell LLM gateway adapter for DSH — OpenAI-compatible, Huawei Cloud AK/SK auth via credential store",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.17",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"license": "Apache-2.0",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"eventsource-parser": "^3.0.0",
|
|
36
|
-
"@huaweidevtools/aishell-dsh-credentials-store": "0.0.
|
|
36
|
+
"@huaweidevtools/aishell-dsh-credentials-store": "0.0.17"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.4",
|