@huaweidevtools/aishell-dsh-llm 0.0.1 → 0.0.2
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 +31 -1
- package/lib/client.js +221 -0
- package/lib/index.js +1338 -29
- package/lib/types/adapter.d.ts +85 -2
- package/lib/types/client/ProviderCard.d.ts +77 -0
- package/lib/types/client/index.d.ts +21 -0
- package/lib/types/images.d.ts +120 -0
- package/lib/types/index.d.ts +69 -5
- package/lib/types/serialize.d.ts +21 -4
- package/lib/types/types.d.ts +36 -13
- package/package.json +33 -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,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The aishell row's Edit-card extension: a one-line readiness status + the
|
|
3
|
+
* live gateway model catalog.
|
|
4
|
+
*
|
|
5
|
+
* The Models page's own editor for an adapter family it does not know renders
|
|
6
|
+
* the host's fallback hint ("other fields live in cordis.patch.yml") with a
|
|
7
|
+
* disabled Apply — nothing to edit, because the aishell profile has no fields
|
|
8
|
+
* the host editor knows how to write. What the user actually needs from this
|
|
9
|
+
* card is the other direction: to READ the family's state.
|
|
10
|
+
*
|
|
11
|
+
* Scope discipline: this page answers "can I use this provider, and what does
|
|
12
|
+
* it carry" — one readiness line whose credential fact comes from
|
|
13
|
+
* `aishell-credentials`' settings namespace (managed at Settings → AI Shell;
|
|
14
|
+
* the card never names accounts or manages anything). The model catalog comes
|
|
15
|
+
* from the gateway through the Host half's `api/aishell/models` route and is
|
|
16
|
+
* likewise read-only — the gateway owns it.
|
|
17
|
+
*
|
|
18
|
+
* @module @huaweidevtools/aishell-dsh-llm/client/ProviderCard
|
|
19
|
+
*/
|
|
20
|
+
import { type ReactNode } from 'react';
|
|
21
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
22
|
+
import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
23
|
+
/** The route body the Host half answers (see its ModelsRouteBody). */
|
|
24
|
+
interface ModelsRouteBody {
|
|
25
|
+
readonly credentialsPresent: boolean;
|
|
26
|
+
readonly models: ReadonlyArray<{
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly inputModalities: readonly string[];
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Credential presence, read from the settings namespace `aishell-credentials`
|
|
33
|
+
* owns. The card only needs the presence flag for its readiness line; the
|
|
34
|
+
* account name stays in the credentials section that owns it.
|
|
35
|
+
*/
|
|
36
|
+
export interface CredentialSnapshot {
|
|
37
|
+
readonly credentialsStored: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Full component props: the seat's owner share + the inject face. */
|
|
40
|
+
export type AishellProviderCardProps = PropsRuntime<'settings.models.provider-card'> & InjectFace<AishellProviderCardInjected>;
|
|
41
|
+
/** Injected hooks bound in the client half's apply(). */
|
|
42
|
+
export interface AishellProviderCardInjected {
|
|
43
|
+
hooks: {
|
|
44
|
+
/** Credential presence from the aishell-client-credentials namespace. */
|
|
45
|
+
credentials: SnapshotStore<CredentialSnapshot>;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Render the aishell family's card extension.
|
|
50
|
+
*
|
|
51
|
+
* The parent renders this inside the host editor's card (below its fallback
|
|
52
|
+
* hint), so the visual style follows the surrounding card rather than opening
|
|
53
|
+
* a panel of its own: one readiness line and one catalog list, no buttons —
|
|
54
|
+
* the host card's Cancel stays the only control. Readiness folds the
|
|
55
|
+
* credential fact into a single conclusion (the question this page asks is
|
|
56
|
+
* "can I use this provider", not "what are my credentials"); details and
|
|
57
|
+
* management of credentials belong to the AI Shell settings section, which
|
|
58
|
+
* the unconfigured hint names.
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* What an empty catalog means, and therefore what the card says about it.
|
|
62
|
+
*
|
|
63
|
+
* An empty model list has two unrelated causes that look identical on the
|
|
64
|
+
* wire: the gateway returned nothing, or the request was never signed because
|
|
65
|
+
* no credentials are stored. Only the second one is actionable, and it is the
|
|
66
|
+
* state every new user starts in — so it has to name the place to fix it.
|
|
67
|
+
*
|
|
68
|
+
* Extracted from the render so the branch is testable without a DOM: the
|
|
69
|
+
* repo's client-side tests cover pure logic, not components.
|
|
70
|
+
*
|
|
71
|
+
* @param catalog - the route body, or `undefined` while it is still loading.
|
|
72
|
+
* @returns the text to show, which the caller renders as-is.
|
|
73
|
+
*/
|
|
74
|
+
export declare function catalogStatusText(catalog: ModelsRouteBody | undefined): string;
|
|
75
|
+
export declare function AishellProviderCard(props: AishellProviderCardProps): ReactNode;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=ProviderCard.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aishell LLM browser half — fills the Models page's provider-card seat.
|
|
3
|
+
*
|
|
4
|
+
* The Models page's Edit card for an adapter family it does not know renders
|
|
5
|
+
* the host's fallback hint and nothing editable. This plugin owns the family,
|
|
6
|
+
* so it fills the card itself through the `settings.models.provider-card`
|
|
7
|
+
* seat (keyed by the `aishell-llm` settings namespace, which the Host half's
|
|
8
|
+
* directory entry names): a read-only credential line (managed by
|
|
9
|
+
* `aishell-credentials`, shown with the verified account name) and the live
|
|
10
|
+
* gateway model catalog, read from the Host half's `api/aishell/models` route
|
|
11
|
+
* — the same cache `listModels` serves, so the card and the pickers cannot
|
|
12
|
+
* disagree about what the gateway carries.
|
|
13
|
+
*
|
|
14
|
+
* @module @huaweidevtools/aishell-dsh-llm/client
|
|
15
|
+
*/
|
|
16
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
17
|
+
export type { AishellProviderCard, AishellProviderCardProps } from './ProviderCard.tsx';
|
|
18
|
+
/** Required services. */
|
|
19
|
+
export declare const inject: string[];
|
|
20
|
+
export declare function apply(ctx: ClientContext): void;
|
|
21
|
+
//# sourceMappingURL=index.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,84 @@
|
|
|
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
|
+
*
|
|
13
18
|
* @module @huaweidevtools/aishell-dsh-llm
|
|
14
19
|
*/
|
|
15
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
20
|
+
import type { Context, Volatile } from '@deepseek-ai/cordis';
|
|
21
|
+
import z from '@deepseek-ai/schemastery';
|
|
22
|
+
import { GatewayAdapter } from './adapter.ts';
|
|
16
23
|
export type { GatewayAdapter, GatewayAdapterOptions } from './adapter.ts';
|
|
17
24
|
/** Plugin config. */
|
|
18
25
|
export interface Config {
|
|
19
26
|
/** Chat API base URL. Default: the public aishell gateway. */
|
|
20
|
-
readonly baseUrl
|
|
21
|
-
/** Credential store service name;
|
|
22
|
-
readonly keyringService
|
|
27
|
+
readonly baseUrl: Volatile<string>;
|
|
28
|
+
/** Credential store service name; empty selects the default `'aishell-dsh'`. */
|
|
29
|
+
readonly keyringService: Volatile<string | undefined>;
|
|
30
|
+
/** Total-pixel cap one request image is projected into. */
|
|
31
|
+
readonly requestImagePixelBudget: Volatile<number>;
|
|
32
|
+
/** Encoded-byte target for one request image. */
|
|
33
|
+
readonly requestImageMaxBytes: Volatile<number>;
|
|
34
|
+
/** Accumulated base64 image bytes one request accepts before offload is required. */
|
|
35
|
+
readonly maxRequestImageBytes: Volatile<number>;
|
|
23
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Settings schema for this entry.
|
|
39
|
+
*
|
|
40
|
+
* The schema exists for two reasons beyond configurability: the settings
|
|
41
|
+
* mirror only lists namespaces whose plugin declares a Config, and the Models
|
|
42
|
+
* page derives a provider row's existence from that listing — without a
|
|
43
|
+
* schema, the `aishell` route registers its adapter but never shows up as a
|
|
44
|
+
* configurable provider.
|
|
45
|
+
*/
|
|
46
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
47
|
+
baseUrl: z<string, string, "volatile-defined">;
|
|
48
|
+
keyringService: z<string, string, "volatile">;
|
|
49
|
+
requestImagePixelBudget: z<number, number, "volatile-defined">;
|
|
50
|
+
requestImageMaxBytes: z<number, number, "volatile-defined">;
|
|
51
|
+
maxRequestImageBytes: z<number, number, "volatile-defined">;
|
|
52
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
53
|
+
baseUrl: z<string, string, "volatile-defined">;
|
|
54
|
+
keyringService: z<string, string, "volatile">;
|
|
55
|
+
requestImagePixelBudget: z<number, number, "volatile-defined">;
|
|
56
|
+
requestImageMaxBytes: z<number, number, "volatile-defined">;
|
|
57
|
+
maxRequestImageBytes: z<number, number, "volatile-defined">;
|
|
58
|
+
}>>, "plain">;
|
|
59
|
+
/**
|
|
60
|
+
* The catalog the route answers with: the gateway's `/models` entries, mapped
|
|
61
|
+
* to the display shape the browser card renders, plus the credential state
|
|
62
|
+
* that decides which of the two postures the card shows. Detached plain data —
|
|
63
|
+
* no secret rides on it, ever (presence flags only).
|
|
64
|
+
*/
|
|
65
|
+
export interface ModelsRouteBody {
|
|
66
|
+
/** Whether a usable AK/SK pair is stored — the fact that separates "empty catalog" from "no credentials". */
|
|
67
|
+
readonly credentialsPresent: boolean;
|
|
68
|
+
readonly models: ReadonlyArray<{
|
|
69
|
+
readonly id: string;
|
|
70
|
+
readonly inputModalities: readonly string[];
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Build the route body from the adapter's own catalog cache, so the card and
|
|
75
|
+
* the pickers cannot disagree about what the gateway carries.
|
|
76
|
+
*
|
|
77
|
+
* `credentialsPresent` is read from the adapter rather than assumed: without
|
|
78
|
+
* credentials `listModels` answers `[]` for a reason that is not "the gateway
|
|
79
|
+
* has no models", and the card's empty state is the one place a fresh user
|
|
80
|
+
* learns which of the two it is.
|
|
81
|
+
*
|
|
82
|
+
* Returned as a plain function of the adapter so a test can pin the mapping
|
|
83
|
+
* without a live context; `apply` wires it to the fetch route.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createModelsRoute(adapter: GatewayAdapter): () => Promise<Response>;
|
|
24
86
|
export declare const name = "aishell-llm";
|
|
25
87
|
export declare const inject: string[];
|
|
26
88
|
/** The provider route this plugin owns. */
|
|
27
89
|
export declare const PROVIDER = "aishell";
|
|
28
|
-
|
|
90
|
+
/** Human-readable provider name the Models page and pickers show. */
|
|
91
|
+
export declare const PROVIDER_DISPLAY_NAME = "AI Shell (Huawei Cloud)";
|
|
92
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
29
93
|
//# sourceMappingURL=index.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 {
|
|
@@ -63,22 +89,19 @@ export interface WireModel {
|
|
|
63
89
|
object?: string;
|
|
64
90
|
owned_by?: string;
|
|
65
91
|
created?: number;
|
|
66
|
-
|
|
92
|
+
/**
|
|
93
|
+
* The gateway's request-modality list (`["text","image"]`) — the catalog's
|
|
94
|
+
* own answer to "what does this model accept", and the only field this
|
|
95
|
+
* adapter reads for capability. A sibling `capabilities` object is not
|
|
96
|
+
* declared: the gateway may send one, and its `image: boolean` reads like a
|
|
97
|
+
* fallback for an absent `modalities`, but nothing here consumes it, so
|
|
98
|
+
* declaring it would only suggest a fallback that does not exist.
|
|
99
|
+
*/
|
|
67
100
|
modalities?: string[];
|
|
68
101
|
reasoning_effort?: string[];
|
|
69
102
|
limits?: WireModelLimits;
|
|
70
103
|
pricing?: WireModelPricing;
|
|
71
104
|
}
|
|
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
105
|
export interface WireModelLimits {
|
|
83
106
|
context_window?: number;
|
|
84
107
|
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.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -17,29 +17,58 @@
|
|
|
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.2"
|
|
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-client-connection": ">=0.2.0-0 <0.3.0-0",
|
|
43
|
+
"@deepseek-ai/dsh-fs": ">=0.2.0-0 <0.3.0-0",
|
|
35
44
|
"@deepseek-ai/dsh-llm": ">=0.2.0-0 <0.3.0-0"
|
|
36
45
|
},
|
|
37
46
|
"devDependencies": {
|
|
38
47
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
48
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.5",
|
|
49
|
+
"@deepseek-ai/dsh-attachment": "0.2.0-rc.1",
|
|
39
50
|
"@deepseek-ai/dsh-brand": ">=0.2.0-0 <0.3.0-0",
|
|
40
|
-
"@deepseek-ai/dsh-
|
|
51
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.1",
|
|
52
|
+
"@deepseek-ai/dsh-client-store": "0.2.0-rc.1",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.1",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.1",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-settings-models": "0.2.0-rc.1",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-fs": "0.2.0-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-llm": ">=0.2.0-0 <0.3.0-0",
|
|
59
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
60
|
+
"@types/react": "~18.3.1",
|
|
61
|
+
"react": "^18.2.0"
|
|
41
62
|
},
|
|
42
63
|
"dsh": {
|
|
64
|
+
"client": {
|
|
65
|
+
"inject": [
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-settings-models",
|
|
68
|
+
"@deepseek-ai/dsh-api-remotes"
|
|
69
|
+
],
|
|
70
|
+
"platform": "web"
|
|
71
|
+
},
|
|
43
72
|
"bundle": {
|
|
44
73
|
"patch": "./cordis.patch.yml"
|
|
45
74
|
}
|