@huaweidevtools/aishell-dsh-llm 0.0.1 → 0.0.3
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 +35 -1
- package/lib/client.js +202 -0
- package/lib/index.js +1328 -31
- package/lib/types/adapter.d.ts +85 -2
- package/lib/types/client/index.d.ts +26 -0
- package/lib/types/client/provider-row-shadow.d.ts +67 -0
- package/lib/types/images.d.ts +120 -0
- package/lib/types/index.d.ts +43 -5
- package/lib/types/provider-name.d.ts +17 -0
- package/lib/types/serialize.d.ts +21 -4
- package/lib/types/types.d.ts +47 -13
- package/package.json +23 -4
package/lib/types/adapter.d.ts
CHANGED
|
@@ -10,13 +10,43 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { LlmAdapter } from '@deepseek-ai/dsh-llm';
|
|
12
12
|
import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
13
|
+
import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
|
|
13
14
|
import type { CredentialsStore } from '@huaweidevtools/aishell-dsh-credentials-store';
|
|
15
|
+
import { type ImageRequestBudget } from './images.ts';
|
|
14
16
|
/** Adapter construction options. */
|
|
15
17
|
export interface GatewayAdapterOptions {
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the current chat API base URL (e.g. `https://api.aishell.openbuildlab.com/v1`).
|
|
20
|
+
*
|
|
21
|
+
* A reader, not a value, for the same reason `resolveAttachments` is: this
|
|
22
|
+
* plugin's whole Config is `Volatile`, so a settings edit is a
|
|
23
|
+
* volatile-only update. The loader commits such an update into the existing
|
|
24
|
+
* references and returns without re-running `apply()`
|
|
25
|
+
* (`cordis-plugin-loader/lib/index.js:381-388`), which means the adapter
|
|
26
|
+
* instance outlives the value it was built with. A captured string would
|
|
27
|
+
* leave the settings surface showing the new endpoint while requests kept
|
|
28
|
+
* going to the old one — silently, and for the life of the process.
|
|
29
|
+
*/
|
|
30
|
+
readonly resolveBaseUrl: () => string;
|
|
18
31
|
/** Credential store for reading AK/SK. */
|
|
19
32
|
readonly keyring: CredentialsStore;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the mounted attachment service, when the deployment has one.
|
|
35
|
+
*
|
|
36
|
+
* Resolved per request rather than captured at construction: the service is
|
|
37
|
+
* provided by a sibling plugin whose fiber may mount after this one, and a
|
|
38
|
+
* captured `undefined` would disable image input for the whole session.
|
|
39
|
+
*/
|
|
40
|
+
readonly resolveAttachments?: () => AttachmentStore | undefined;
|
|
41
|
+
/** Mounted filesystem provider, mapped into the tool execution world for image handle paths. */
|
|
42
|
+
readonly resolveHostPath?: (hostPath: string) => string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the per-image projection policy, read per request so a budget edit
|
|
45
|
+
* takes effect without a reload — same reasoning as `resolveBaseUrl`.
|
|
46
|
+
*/
|
|
47
|
+
readonly resolveImageBudget: () => ImageRequestBudget;
|
|
48
|
+
/** Resolve accumulated represented base64 bytes one request may carry before images must be offloaded. */
|
|
49
|
+
readonly resolveMaxRequestImageBytes: () => number;
|
|
20
50
|
}
|
|
21
51
|
export declare class GatewayAdapter extends LlmAdapter {
|
|
22
52
|
private readonly opts;
|
|
@@ -34,6 +64,17 @@ export declare class GatewayAdapter extends LlmAdapter {
|
|
|
34
64
|
private fetchWireModels;
|
|
35
65
|
listModels(provider: string): Promise<readonly LlmModelInfo[]>;
|
|
36
66
|
resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
|
|
67
|
+
/**
|
|
68
|
+
* Whether a usable AK/SK pair is stored.
|
|
69
|
+
*
|
|
70
|
+
* The one predicate behind every "can this route do anything" question:
|
|
71
|
+
* `fetchWireModels` returns an empty catalog without it, and the Models-page
|
|
72
|
+
* card reports readiness from it. Kept in one place so the catalog and the
|
|
73
|
+
* card cannot disagree about whether this route is usable.
|
|
74
|
+
*
|
|
75
|
+
* @returns whether both halves of the signing credential are present.
|
|
76
|
+
*/
|
|
77
|
+
hasCredentials(): boolean;
|
|
37
78
|
/** Read AK/SK (and optional SecurityToken) from the credential store. */
|
|
38
79
|
private readCredentials;
|
|
39
80
|
/** Read credentials or throw MISSING_CREDENTIAL — every signing code path goes through here. */
|
|
@@ -43,6 +84,48 @@ export declare class GatewayAdapter extends LlmAdapter {
|
|
|
43
84
|
* signing flow: read credentials → sign → attach headers → fetch.
|
|
44
85
|
*/
|
|
45
86
|
private signedFetch;
|
|
87
|
+
/**
|
|
88
|
+
* Diagnose an "unsupported model" rejection and append what to do about it.
|
|
89
|
+
*
|
|
90
|
+
* The gateway answers a model id it does not carry with `AISHELL.1004`
|
|
91
|
+
* (`model_not_found`, HTTP 404 — see {@link isModelNotFound}). The default
|
|
92
|
+
* selection is a pinned literal (it must be readable before any credential or
|
|
93
|
+
* network call could enumerate the catalog), so the day the catalog drops
|
|
94
|
+
* that id, every fresh session fails here. Rather than leave the user to
|
|
95
|
+
* reverse-engineer the supported set, the failure itself asks the gateway
|
|
96
|
+
* (the catalog cache usually makes this a second signed GET already in
|
|
97
|
+
* flight) and names the models it currently accepts.
|
|
98
|
+
*
|
|
99
|
+
* Returns '' for any other failure, so the message stays untouched there.
|
|
100
|
+
*/
|
|
101
|
+
private unsupportedModelHint;
|
|
102
|
+
/**
|
|
103
|
+
* Resolve one image occurrence's execution-world read path, when the
|
|
104
|
+
* deployment exposes one. Only enriches the handle text written beside an
|
|
105
|
+
* image; a deployment without a filesystem provider simply yields the
|
|
106
|
+
* shorter form.
|
|
107
|
+
*
|
|
108
|
+
* Deliberately unguarded, like the official adapters: this throws only for
|
|
109
|
+
* an invalid durable reference, and every reference reaching here was
|
|
110
|
+
* already validated by the `readImageRequest` that loaded its bytes — so a
|
|
111
|
+
* throw means the attachment record is inconsistent, which should surface
|
|
112
|
+
* rather than be absorbed into less informative handle text.
|
|
113
|
+
*/
|
|
114
|
+
private readonly imageAccess;
|
|
115
|
+
/**
|
|
116
|
+
* Turn the harness history into the wire body, loading image bytes on the
|
|
117
|
+
* way.
|
|
118
|
+
*
|
|
119
|
+
* Deliberately the whole image pipeline and nothing else, so `stream` stays
|
|
120
|
+
* about transport. Ordering matters and mirrors the official adapters:
|
|
121
|
+
* resolve the attachment service, prepare one request version per retained
|
|
122
|
+
* image, fail the request if the budget is exceeded (handing
|
|
123
|
+
* `dsh-compaction-image-offload` its decision), and only then project the
|
|
124
|
+
* offloaded occurrences to placeholder text — a mark set by an earlier
|
|
125
|
+
* request's offload decision, which serialization must replace rather than
|
|
126
|
+
* look up bytes for.
|
|
127
|
+
*/
|
|
128
|
+
private buildBody;
|
|
46
129
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
47
130
|
}
|
|
48
131
|
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aishell LLM browser half — shadows the Models page's provider row.
|
|
3
|
+
*
|
|
4
|
+
* The page's row for the aishell family is read-only: its profile has no
|
|
5
|
+
* fields the host editor knows how to write, and its credentials live in
|
|
6
|
+
* `aishell-credentials`' own settings section. What the row should tell the
|
|
7
|
+
* user is the one fact the old card extension distilled — ready or not — so
|
|
8
|
+
* the browser half paints it as a status dot beside the row's name and hides
|
|
9
|
+
* the header's Edit button (see `client/provider-row-shadow.ts`).
|
|
10
|
+
*
|
|
11
|
+
* The readiness fact is read from the `aishell-client-credentials` settings
|
|
12
|
+
* namespace (`aishell-credentials` owns it; the shadow never names accounts
|
|
13
|
+
* or manages anything). The shadow follows that namespace through
|
|
14
|
+
* `whileServed`, so a deployment without that plugin (this LLM package
|
|
15
|
+
* installed standalone) shows no trace of the dot either — the fact is
|
|
16
|
+
* unknown, not false.
|
|
17
|
+
*
|
|
18
|
+
* @module @huaweidevtools/aishell-dsh-llm/client
|
|
19
|
+
*/
|
|
20
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
21
|
+
export type { ProviderRowShadowDisposer, } from './provider-row-shadow.ts';
|
|
22
|
+
export { installProviderRowShadow, ROW_DOT_MARKER, ROW_EDIT_HIDDEN, } from './provider-row-shadow.ts';
|
|
23
|
+
/** Required services. */
|
|
24
|
+
export declare const inject: string[];
|
|
25
|
+
export declare function apply(ctx: ClientContext): void;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime shadow of the aishell provider's Models-page row.
|
|
3
|
+
*
|
|
4
|
+
* The Models page renders each configured provider as a row whose header
|
|
5
|
+
* (`rowHead`) shows the display name followed by an Edit button the aishell
|
|
6
|
+
* family has no use for: the profile has no fields the host editor knows how
|
|
7
|
+
* to write (the old card extension said so itself), and the row's directory
|
|
8
|
+
* entry names no api-key credential, so the host header also paints no status
|
|
9
|
+
* dot. What the user actually needs from this row is the one fact the old
|
|
10
|
+
* card distilled — ready or not — and that reads better beside the name than
|
|
11
|
+
* in a card below the header.
|
|
12
|
+
*
|
|
13
|
+
* 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
|
+
* area BELOW the header, which is exactly the part this family no longer
|
|
16
|
+
* wants). The shadow therefore works the way aishell-brand's host residues
|
|
17
|
+
* do: find the row, patch the DOM. The row is located by its rendered name
|
|
18
|
+
* (the directory entry's displayName, `PROVIDER_DISPLAY_NAME` — a literal
|
|
19
|
+
* both halves share); the class names beside it are CSS-module hashes, so
|
|
20
|
+
* they are matched by the same `[class*="…"]` substring selectors the hero
|
|
21
|
+
* patch already relies on.
|
|
22
|
+
*
|
|
23
|
+
* What the shadow does to a found row:
|
|
24
|
+
*
|
|
25
|
+
* 1. a status dot after the name — the host credential dot's own 8px
|
|
26
|
+
* geometry and success/warn colors, green when AK/SK are stored, warn
|
|
27
|
+
* when they are not, with the readiness copy as its title (the row
|
|
28
|
+
* keeps saying what the old card said, just in one word of hover);
|
|
29
|
+
* 2. the row's Edit button is hidden — the row is read-only, and the only
|
|
30
|
+
* control the header keeps is whatever the host itself decides (Remove,
|
|
31
|
+
* for user-added rows; this family's row is not removable). The hide is
|
|
32
|
+
* a marker-keyed `display: none !important` rule, not the `hidden`
|
|
33
|
+
* attribute: the button's own class sets `display: inline-flex`, and an
|
|
34
|
+
* author declaration beats the UA sheet's `[hidden] { display: none }`,
|
|
35
|
+
* so the attribute alone leaves the button visible.
|
|
36
|
+
*
|
|
37
|
+
* The Models page re-renders the rows (credential saves, pushed
|
|
38
|
+
* invalidations), so a MutationObserver keeps the shadow applied for as long
|
|
39
|
+
* as the page exists. Shadowing is a default, not a force: install() and the
|
|
40
|
+
* disposer it returns are symmetric, so disabling the plugin restores the
|
|
41
|
+
* official row exactly — dot removed, Edit button back.
|
|
42
|
+
*
|
|
43
|
+
* @module @huaweidevtools/aishell-dsh-llm/client/provider-row-shadow
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* The disposer stops the observer, removes every dot the install added, and
|
|
47
|
+
* restores every hidden Edit button; a no-op install returns this.
|
|
48
|
+
*/
|
|
49
|
+
export type ProviderRowShadowDisposer = () => void;
|
|
50
|
+
/** Marker attribute the dot carries (exposed for the spec). */
|
|
51
|
+
export declare const ROW_DOT_MARKER = "data-aishell-provider-dot";
|
|
52
|
+
/** Marker attribute a hidden Edit button carries (exposed for the spec). */
|
|
53
|
+
export declare const ROW_EDIT_HIDDEN = "data-aishell-edit-hidden";
|
|
54
|
+
/** The dot's own CSS: the host credential dot's geometry and palette. */
|
|
55
|
+
export declare const ROW_DOT_CSS: string;
|
|
56
|
+
/**
|
|
57
|
+
* Install the provider-row shadow on a document.
|
|
58
|
+
*
|
|
59
|
+
* `ready` is the credential fact as the browser half derives it from the
|
|
60
|
+
* settings namespace `aishell-credentials` owns (see `client/index.ts`);
|
|
61
|
+
* the install does not read the store itself, so a later credential save is
|
|
62
|
+
* a reinstall with the new fact, not a re-derivation buried in here. Sweeps
|
|
63
|
+
* the page once, then observes the body so re-rendered rows are covered too.
|
|
64
|
+
* Returns the disposer that stops the observer and restores every row.
|
|
65
|
+
*/
|
|
66
|
+
export declare function installProviderRowShadow(target: Document, ready: boolean): ProviderRowShadowDisposer;
|
|
67
|
+
//# sourceMappingURL=provider-row-shadow.d.ts.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-image preparation for the aishell gateway adapter.
|
|
3
|
+
*
|
|
4
|
+
* The harness hands an adapter durable *references*, never bytes: an
|
|
5
|
+
* {@link ImageBlock} carries an opaque `attachmentId` that is explicitly
|
|
6
|
+
* "never a filesystem path or bearer URL". Turning one into the base64 payload
|
|
7
|
+
* an OpenAI `image_url` part needs is therefore an asynchronous read against
|
|
8
|
+
* the mounted attachment service — which is why this runs as a step ahead of
|
|
9
|
+
* serialization rather than inside it.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors the official adapters' shape (`dsh-llm-pi-ai`'s `collectImageRefs` /
|
|
12
|
+
* `prepareRequestImages`, `dsh-llm-deepseek`'s `prepareImages`): collect the
|
|
13
|
+
* retained references, ask the attachment service for one deterministic
|
|
14
|
+
* request version each, and let the route's budget decide whether the request
|
|
15
|
+
* fits.
|
|
16
|
+
*
|
|
17
|
+
* @module @huaweidevtools/aishell-dsh-llm/images
|
|
18
|
+
*/
|
|
19
|
+
import type { AttachmentStore, ImageAttachmentRef, RequestImageAttachment } from '@deepseek-ai/dsh-attachment';
|
|
20
|
+
import type { ImageAttachmentAccessResolver, RequestMessage } from '@deepseek-ai/dsh-llm';
|
|
21
|
+
/** Key under which one durable image's request version is held. */
|
|
22
|
+
export type ImageKey = ImageAttachmentRef['attachmentId'];
|
|
23
|
+
/** Request versions by durable attachment id, as {@link prepareRequestImages} returns them. */
|
|
24
|
+
export type RequestImages = ReadonlyMap<ImageKey, RequestImageAttachment>;
|
|
25
|
+
/**
|
|
26
|
+
* The route's per-image projection policy: the total-pixel cap one image is
|
|
27
|
+
* scaled into, and the encoded-byte target the encoder aims for. Both are
|
|
28
|
+
* config fields on this plugin's Config, sharing the pi-ai defaults.
|
|
29
|
+
*/
|
|
30
|
+
export interface ImageRequestBudget {
|
|
31
|
+
/** Positive width-times-height cap one request image is projected into. */
|
|
32
|
+
readonly maxPixels: number;
|
|
33
|
+
/** Encoded-byte target per request image; the smallest ladder output wins when none fits. */
|
|
34
|
+
readonly maxBytes: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Execution-world path resolver, re-exported under this module's vocabulary.
|
|
38
|
+
* The official type rather than a local restatement: it is what
|
|
39
|
+
* `requestImageHandleText` / `offloadedImageText` accept, so an ad-hoc copy
|
|
40
|
+
* could drift from the contract it is passed into.
|
|
41
|
+
*/
|
|
42
|
+
export type ImageAccessResolver = ImageAttachmentAccessResolver;
|
|
43
|
+
/**
|
|
44
|
+
* True when any message carries an image block — the guard that decides
|
|
45
|
+
* whether this adapter needs the attachment service at all.
|
|
46
|
+
*
|
|
47
|
+
* @param messages - complete request history.
|
|
48
|
+
* @returns whether any message content holds an image.
|
|
49
|
+
*/
|
|
50
|
+
export declare function historyHasImage(messages: readonly RequestMessage[]): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Reject image blocks in roles the wire format cannot carry them in.
|
|
53
|
+
*
|
|
54
|
+
* A user or tool-result message maps onto content parts, so an image there is
|
|
55
|
+
* representable. Every other role serializes to a plain string (assistant
|
|
56
|
+
* text plus tool calls, system/developer prose), where an image would be
|
|
57
|
+
* silently dropped — the exact failure this module exists to remove, so it
|
|
58
|
+
* fails loud instead.
|
|
59
|
+
*
|
|
60
|
+
* @param messages - complete request history.
|
|
61
|
+
* @throws LlmError `UNSUPPORTED_CONTENT` naming the offending role.
|
|
62
|
+
*/
|
|
63
|
+
export declare function assertImageRoles(messages: readonly RequestMessage[]): void;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve one request version per retained image, keyed by durable attachment
|
|
66
|
+
* id so a reference shared by several occurrences is read once.
|
|
67
|
+
*
|
|
68
|
+
* Occurrences already marked `offloaded` are skipped: their bytes are not sent
|
|
69
|
+
* (serialization replaces them with placeholder text), so reading them would
|
|
70
|
+
* be work whose result nothing consumes.
|
|
71
|
+
*
|
|
72
|
+
* @param messages - complete request history.
|
|
73
|
+
* @param attachments - mounted attachment service.
|
|
74
|
+
* @param budget - projection policy for this route.
|
|
75
|
+
* @param signal - request cancellation, honoured by the attachment read.
|
|
76
|
+
* @returns request versions by durable attachment id.
|
|
77
|
+
*/
|
|
78
|
+
export declare function prepareRequestImages(messages: readonly RequestMessage[], attachments: AttachmentStore, budget: ImageRequestBudget, signal?: AbortSignal): Promise<RequestImages>;
|
|
79
|
+
/**
|
|
80
|
+
* Request version for one retained occurrence, or a loud failure.
|
|
81
|
+
*
|
|
82
|
+
* Every image block reaching serialization must have been prepared, so a
|
|
83
|
+
* miss is a bug in this adapter rather than bad input — but the alternative
|
|
84
|
+
* (letting the lookup produce `undefined` and reading `.bytes` off it) turns
|
|
85
|
+
* it into a `TypeError` from deep inside the request body, so it is named.
|
|
86
|
+
*
|
|
87
|
+
* @param images - prepared request versions.
|
|
88
|
+
* @param ref - durable reference of one retained occurrence.
|
|
89
|
+
* @returns the request version carrying that image's bytes.
|
|
90
|
+
* @throws LlmError `INVALID_REQUEST` when the reference was never prepared.
|
|
91
|
+
*/
|
|
92
|
+
export declare function requireRequestImage(images: RequestImages, ref: ImageAttachmentRef): RequestImageAttachment;
|
|
93
|
+
/**
|
|
94
|
+
* Replace every offloaded occurrence with its placeholder text.
|
|
95
|
+
*
|
|
96
|
+
* The durable `offloaded` mark is a surface fact shared by all routes; only
|
|
97
|
+
* the placeholder wording is route-owned. Request assembly does not do this
|
|
98
|
+
* for adapters, so an adapter that skips it would send an image whose bytes
|
|
99
|
+
* it deliberately never loaded.
|
|
100
|
+
*
|
|
101
|
+
* @param messages - complete request history.
|
|
102
|
+
* @param resolveAccess - execution-world path resolver, when one is available.
|
|
103
|
+
* @returns the history with offloaded occurrences projected to text.
|
|
104
|
+
*/
|
|
105
|
+
export declare function projectOffloaded(messages: readonly RequestMessage[], resolveAccess?: ImageAccessResolver): readonly RequestMessage[];
|
|
106
|
+
/**
|
|
107
|
+
* Fail the request when its retained images exceed the route's byte budget.
|
|
108
|
+
*
|
|
109
|
+
* This adapter does not offload on its own: the durable decision belongs to
|
|
110
|
+
* the surface, and `dsh-compaction-image-offload` (mounted by `dsh-base`) is
|
|
111
|
+
* the component that records it and retries the step. Throwing the canonical
|
|
112
|
+
* code with the required count is what hands it that decision.
|
|
113
|
+
*
|
|
114
|
+
* @param messages - complete request history, carrying the surface's offload marks.
|
|
115
|
+
* @param images - prepared request versions.
|
|
116
|
+
* @param maxBytes - accumulated represented base64 bytes this route accepts.
|
|
117
|
+
* @throws LlmError `IMAGE_OFFLOAD_REQUIRED` carrying how many more occurrences must be offloaded.
|
|
118
|
+
*/
|
|
119
|
+
export declare function assertRequestImageBudget(messages: readonly RequestMessage[], images: RequestImages, maxBytes: number): void;
|
|
120
|
+
//# sourceMappingURL=images.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -10,20 +10,58 @@
|
|
|
10
10
|
* Credential management (settings namespace + watch → store transfer) lives
|
|
11
11
|
* in `aishell-credentials`; this plugin only consumes the stored credentials.
|
|
12
12
|
*
|
|
13
|
+
* This plugin also declares the `aishell` route in the configurable-provider
|
|
14
|
+
* directory (`registerConfigurableProviders`), which is what the Models page
|
|
15
|
+
* joins with live routes — and it carries a Config schema, without which the
|
|
16
|
+
* settings mirror (and therefore the Models row) never lists this namespace.
|
|
17
|
+
* The row itself carries no card: the browser half replaces the page's
|
|
18
|
+
* Edit-card extension with a status dot beside the row's name (see
|
|
19
|
+
* `client/provider-row-shadow.ts`).
|
|
20
|
+
*
|
|
13
21
|
* @module @huaweidevtools/aishell-dsh-llm
|
|
14
22
|
*/
|
|
15
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
23
|
+
import type { Context, Volatile } from '@deepseek-ai/cordis';
|
|
24
|
+
import z from '@deepseek-ai/schemastery';
|
|
16
25
|
export type { GatewayAdapter, GatewayAdapterOptions } from './adapter.ts';
|
|
17
26
|
/** Plugin config. */
|
|
18
27
|
export interface Config {
|
|
19
28
|
/** Chat API base URL. Default: the public aishell gateway. */
|
|
20
|
-
readonly baseUrl
|
|
21
|
-
/** Credential store service name;
|
|
22
|
-
readonly keyringService
|
|
29
|
+
readonly baseUrl: Volatile<string>;
|
|
30
|
+
/** Credential store service name; empty selects the default `'aishell-dsh'`. */
|
|
31
|
+
readonly keyringService: Volatile<string | undefined>;
|
|
32
|
+
/** Total-pixel cap one request image is projected into. */
|
|
33
|
+
readonly requestImagePixelBudget: Volatile<number>;
|
|
34
|
+
/** Encoded-byte target for one request image. */
|
|
35
|
+
readonly requestImageMaxBytes: Volatile<number>;
|
|
36
|
+
/** Accumulated base64 image bytes one request accepts before offload is required. */
|
|
37
|
+
readonly maxRequestImageBytes: Volatile<number>;
|
|
23
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Settings schema for this entry.
|
|
41
|
+
*
|
|
42
|
+
* The schema exists for two reasons beyond configurability: the settings
|
|
43
|
+
* mirror only lists namespaces whose plugin declares a Config, and the Models
|
|
44
|
+
* page derives a provider row's existence from that listing — without a
|
|
45
|
+
* schema, the `aishell` route registers its adapter but never shows up as a
|
|
46
|
+
* configurable provider.
|
|
47
|
+
*/
|
|
48
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
49
|
+
baseUrl: z<string, string, "volatile-defined">;
|
|
50
|
+
keyringService: z<string, string, "volatile">;
|
|
51
|
+
requestImagePixelBudget: z<number, number, "volatile-defined">;
|
|
52
|
+
requestImageMaxBytes: z<number, number, "volatile-defined">;
|
|
53
|
+
maxRequestImageBytes: z<number, number, "volatile-defined">;
|
|
54
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
55
|
+
baseUrl: z<string, string, "volatile-defined">;
|
|
56
|
+
keyringService: z<string, string, "volatile">;
|
|
57
|
+
requestImagePixelBudget: z<number, number, "volatile-defined">;
|
|
58
|
+
requestImageMaxBytes: z<number, number, "volatile-defined">;
|
|
59
|
+
maxRequestImageBytes: z<number, number, "volatile-defined">;
|
|
60
|
+
}>>, "plain">;
|
|
24
61
|
export declare const name = "aishell-llm";
|
|
25
62
|
export declare const inject: string[];
|
|
26
63
|
/** The provider route this plugin owns. */
|
|
27
64
|
export declare const PROVIDER = "aishell";
|
|
28
|
-
export
|
|
65
|
+
export { PROVIDER_DISPLAY_NAME } from './provider-name.ts';
|
|
66
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
29
67
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The aishell provider's display name, shared by both plugin halves.
|
|
3
|
+
*
|
|
4
|
+
* The Host half registers it on the configurable-provider directory (the
|
|
5
|
+
* Models page renders it as the provider row's name), and the browser half's
|
|
6
|
+
* row shadow finds that row by matching the rendered name against this
|
|
7
|
+
* literal (see `client/provider-row-shadow.ts`). It must stay a single
|
|
8
|
+
* literal string — the shadow compares `textContent` with it.
|
|
9
|
+
*
|
|
10
|
+
* It is also the name `providerInfo()` reports, so the picker and the Models
|
|
11
|
+
* row call the route the same thing: this package's whole surface is one
|
|
12
|
+
* product, and qualifying it by cloud ("AI Shell (Huawei Cloud)") added a
|
|
13
|
+
* distinction no reader needed — there is no second AI Shell to tell it
|
|
14
|
+
* apart from.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PROVIDER_DISPLAY_NAME = "AI Shell";
|
|
17
|
+
//# sourceMappingURL=provider-name.d.ts.map
|
package/lib/types/serialize.d.ts
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Serialize harness {@link GenerateOptions} into an OpenAI-compatible wire request.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Messages without images serialize to a plain string, exactly as before image
|
|
5
|
+
* support existed. A message carrying an image serializes to an ordered part
|
|
6
|
+
* list instead — a handle-text part naming the image, then the image itself as
|
|
7
|
+
* a base64 data URL — which is the only shape an OpenAI-compatible endpoint
|
|
8
|
+
* accepts image input in.
|
|
9
|
+
*
|
|
10
|
+
* Image bytes are not read here. The harness hands over durable references,
|
|
11
|
+
* not bytes, so `prepareRequestImages` resolves them one step ahead of this
|
|
12
|
+
* call and passes the result in; that keeps this function synchronous and
|
|
13
|
+
* keeps the one await in the adapter, where the attachment service is
|
|
14
|
+
* reachable.
|
|
6
15
|
*
|
|
7
16
|
* @module @huaweidevtools/aishell-dsh-llm/serialize
|
|
8
17
|
*/
|
|
9
18
|
import type { GenerateOptions } from '@deepseek-ai/dsh-llm';
|
|
19
|
+
import { type ImageAccessResolver, type RequestImages } from './images.ts';
|
|
10
20
|
import type { WireRequest } from './types.ts';
|
|
11
|
-
/**
|
|
12
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Build the POST body for `/chat/completions`.
|
|
23
|
+
*
|
|
24
|
+
* @param options - provider-neutral request.
|
|
25
|
+
* @param images - prepared request versions, required only when the history carries a retained image.
|
|
26
|
+
* @param resolveAccess - execution-world path resolver for image handle text, when a provider exposes one.
|
|
27
|
+
* @returns the OpenAI-compatible request body.
|
|
28
|
+
*/
|
|
29
|
+
export declare function serializeRequest(options: GenerateOptions, images?: RequestImages, resolveAccess?: ImageAccessResolver): WireRequest;
|
|
13
30
|
//# sourceMappingURL=serialize.d.ts.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenAI-compatible chat-completions wire format
|
|
2
|
+
* OpenAI-compatible chat-completions wire format.
|
|
3
3
|
*
|
|
4
4
|
* @module @huaweidevtools/aishell-dsh-llm/types
|
|
5
5
|
*/
|
|
@@ -16,6 +16,25 @@ export interface WireRequest {
|
|
|
16
16
|
max_tokens?: number;
|
|
17
17
|
stop?: string[];
|
|
18
18
|
}
|
|
19
|
+
/** One message's content: plain text, or an ordered part list when it carries an image. */
|
|
20
|
+
export type WireContent = string | WireContentPart[];
|
|
21
|
+
/**
|
|
22
|
+
* One content part. Only the two shapes this adapter emits are declared: text,
|
|
23
|
+
* and the base64 data URL an OpenAI-compatible endpoint reads an image from.
|
|
24
|
+
* The harness's `reasoning` blocks fold into text; `file` blocks are projected
|
|
25
|
+
* to handle text by request assembly before any adapter sees them.
|
|
26
|
+
*/
|
|
27
|
+
export type WireContentPart = WireTextPart | WireImageUrlPart;
|
|
28
|
+
export interface WireTextPart {
|
|
29
|
+
type: 'text';
|
|
30
|
+
text: string;
|
|
31
|
+
}
|
|
32
|
+
export interface WireImageUrlPart {
|
|
33
|
+
type: 'image_url';
|
|
34
|
+
image_url: {
|
|
35
|
+
url: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
19
38
|
/** One wire message. */
|
|
20
39
|
export type WireMessage = WireSystemMessage | WireUserMessage | WireAssistantMessage | WireToolMessage;
|
|
21
40
|
export interface WireSystemMessage {
|
|
@@ -24,7 +43,7 @@ export interface WireSystemMessage {
|
|
|
24
43
|
}
|
|
25
44
|
export interface WireUserMessage {
|
|
26
45
|
role: 'user';
|
|
27
|
-
content:
|
|
46
|
+
content: WireContent;
|
|
28
47
|
}
|
|
29
48
|
export interface WireAssistantMessage {
|
|
30
49
|
role: 'assistant';
|
|
@@ -34,6 +53,13 @@ export interface WireAssistantMessage {
|
|
|
34
53
|
export interface WireToolMessage {
|
|
35
54
|
role: 'tool';
|
|
36
55
|
tool_call_id: string;
|
|
56
|
+
/**
|
|
57
|
+
* Always a string, even when the tool result carried an image. OpenAI's
|
|
58
|
+
* tool message takes text only; an image from a tool result is hoisted into
|
|
59
|
+
* a separate `user` turn instead, which is what the one OpenAI-path
|
|
60
|
+
* serializer in the ecosystem does. Widening this field would produce a
|
|
61
|
+
* request that some gateways reject with 400 and others silently drop.
|
|
62
|
+
*/
|
|
37
63
|
content: string;
|
|
38
64
|
}
|
|
39
65
|
export interface WireAssistantToolCall {
|
|
@@ -60,25 +86,33 @@ export interface WireChunk {
|
|
|
60
86
|
/** One model entry from the GET /models response (OpenAI-compatible). */
|
|
61
87
|
export interface WireModel {
|
|
62
88
|
id: string;
|
|
89
|
+
/**
|
|
90
|
+
* Display name, when the gateway sends one. Outside OpenAI's own model
|
|
91
|
+
* object (which carries `id` alone), so it is an extension like the
|
|
92
|
+
* metadata below, and the gateway omits it for a model that declares none.
|
|
93
|
+
*
|
|
94
|
+
* This is the field that lets an id stay a durable routing key while the
|
|
95
|
+
* label a person reads stays free to change — the split every official
|
|
96
|
+
* adapter's `name ?? id` expresses. Without it the two roles collapse onto
|
|
97
|
+
* the id, and a reserved name like `trial` has to read well in a picker.
|
|
98
|
+
*/
|
|
99
|
+
name?: string;
|
|
63
100
|
object?: string;
|
|
64
101
|
owned_by?: string;
|
|
65
102
|
created?: number;
|
|
66
|
-
|
|
103
|
+
/**
|
|
104
|
+
* The gateway's request-modality list (`["text","image"]`) — the catalog's
|
|
105
|
+
* own answer to "what does this model accept", and the only field this
|
|
106
|
+
* adapter reads for capability. A sibling `capabilities` object is not
|
|
107
|
+
* declared: the gateway may send one, and its `image: boolean` reads like a
|
|
108
|
+
* fallback for an absent `modalities`, but nothing here consumes it, so
|
|
109
|
+
* declaring it would only suggest a fallback that does not exist.
|
|
110
|
+
*/
|
|
67
111
|
modalities?: string[];
|
|
68
112
|
reasoning_effort?: string[];
|
|
69
113
|
limits?: WireModelLimits;
|
|
70
114
|
pricing?: WireModelPricing;
|
|
71
115
|
}
|
|
72
|
-
export interface WireModelCapabilities {
|
|
73
|
-
text?: boolean;
|
|
74
|
-
image?: boolean;
|
|
75
|
-
video?: boolean;
|
|
76
|
-
audio?: boolean;
|
|
77
|
-
streaming?: boolean;
|
|
78
|
-
thinking?: boolean;
|
|
79
|
-
tool_call?: boolean;
|
|
80
|
-
embedding?: boolean;
|
|
81
|
-
}
|
|
82
116
|
export interface WireModelLimits {
|
|
83
117
|
context_window?: number;
|
|
84
118
|
max_prompt_tokens?: number;
|
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.3",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -17,29 +17,48 @@
|
|
|
17
17
|
".": {
|
|
18
18
|
"types": "./lib/types/index.d.ts",
|
|
19
19
|
"default": "./lib/index.js"
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./lib/types/client/index.d.ts",
|
|
23
|
+
"default": "./lib/client.js"
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
21
26
|
},
|
|
22
27
|
"files": [
|
|
23
28
|
"lib/index.js",
|
|
29
|
+
"lib/client.js",
|
|
24
30
|
"lib/types/**/*.d.ts",
|
|
25
31
|
"cordis.patch.yml"
|
|
26
32
|
],
|
|
27
33
|
"license": "Apache-2.0",
|
|
28
34
|
"dependencies": {
|
|
29
35
|
"eventsource-parser": "^3.0.0",
|
|
30
|
-
"@huaweidevtools/aishell-dsh-credentials-store": "0.0.
|
|
36
|
+
"@huaweidevtools/aishell-dsh-credentials-store": "0.0.3"
|
|
31
37
|
},
|
|
32
38
|
"peerDependencies": {
|
|
33
39
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
40
|
+
"@deepseek-ai/dsh-attachment": ">=0.2.0-0 <0.3.0-0",
|
|
34
41
|
"@deepseek-ai/dsh-brand": ">=0.2.0-0 <0.3.0-0",
|
|
42
|
+
"@deepseek-ai/dsh-fs": ">=0.2.0-0 <0.3.0-0",
|
|
35
43
|
"@deepseek-ai/dsh-llm": ">=0.2.0-0 <0.3.0-0"
|
|
36
44
|
},
|
|
37
45
|
"devDependencies": {
|
|
38
46
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
47
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.5",
|
|
48
|
+
"@deepseek-ai/dsh-attachment": "0.2.0-rc.1",
|
|
39
49
|
"@deepseek-ai/dsh-brand": ">=0.2.0-0 <0.3.0-0",
|
|
40
|
-
"@deepseek-ai/dsh-
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.1",
|
|
51
|
+
"@deepseek-ai/dsh-fs": "0.2.0-rc.1",
|
|
52
|
+
"@deepseek-ai/dsh-llm": ">=0.2.0-0 <0.3.0-0",
|
|
53
|
+
"@deepseek-ai/schemastery": "3.18.4"
|
|
41
54
|
},
|
|
42
55
|
"dsh": {
|
|
56
|
+
"client": {
|
|
57
|
+
"inject": [
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
59
|
+
],
|
|
60
|
+
"platform": "web"
|
|
61
|
+
},
|
|
43
62
|
"bundle": {
|
|
44
63
|
"patch": "./cordis.patch.yml"
|
|
45
64
|
}
|