@huaweidevtools/aishell-dsh-llm 0.0.1-beta.2 → 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.
@@ -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
- /** Chat API base URL (e.g. `https://api.aishell.openbuildlab.com`). */
17
- readonly baseUrl: string;
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
@@ -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?: string;
21
- /** Credential store service name; defaults to `'aishell-dsh'`. */
22
- readonly keyringService?: string;
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
- export declare function apply(ctx: Context, config?: Config): void;
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
@@ -1,13 +1,30 @@
1
1
  /**
2
2
  * Serialize harness {@link GenerateOptions} into an OpenAI-compatible wire request.
3
3
  *
4
- * Text-only: image blocks are not mapped (the gateway adapter is for text
5
- * models; image support can be added later by extending `blocksToText`).
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
- /** Build the POST body for `/chat/completions`. */
12
- export declare function serializeRequest(options: GenerateOptions): WireRequest;
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OpenAI-compatible chat-completions wire format (text-only).
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: string;
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
- capabilities?: WireModelCapabilities;
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.1-beta.2",
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.1-beta.2"
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-llm": ">=0.2.0-0 <0.3.0-0"
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
  }