@adaptiqai/deepseek-harness 0.2.11 → 0.2.13
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 +3 -0
- package/lib/client.js +2 -2
- package/lib/host.js +409 -56
- package/lib/types/host/adapter.d.ts +15 -0
- package/lib/types/host/images.d.ts +91 -0
- package/lib/types/host/release-age.d.ts +57 -0
- package/lib/types/host/wire.d.ts +25 -7
- package/package.json +2 -2
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm';
|
|
8
8
|
import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, PreparedAdapterCall, ResolvedRetryPolicy, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
9
9
|
import { type AdaptiqFailure, type AuthManager, type CatalogModel, type RequestEvents, type UserTurn } from '@adaptiqai/core';
|
|
10
|
+
import { type ImageLimits, type ImageRef, type ImageSource } from './images.ts';
|
|
10
11
|
import { type ParentSession, type ReferencedSession } from './wire.ts';
|
|
11
12
|
/** What the adapter tells the rest of the plugin about each request. */
|
|
12
13
|
export interface AdapterDeps {
|
|
@@ -42,6 +43,14 @@ export interface AdapterDeps {
|
|
|
42
43
|
readonly events: RequestEvents;
|
|
43
44
|
/** Whether the server behind a model puts idle conversations to sleep (it reports session history apart). */
|
|
44
45
|
readonly autoSleep?: (model: string) => boolean;
|
|
46
|
+
/** The harness's attachment service, where attached images are stored (absent on hosts without one). */
|
|
47
|
+
attachments?(): ImageSource | undefined;
|
|
48
|
+
/** Where the model's tools can read the stored copy of an image. */
|
|
49
|
+
imageAccess?(ref: ImageRef): {
|
|
50
|
+
readonlyPath: string;
|
|
51
|
+
} | undefined;
|
|
52
|
+
/** The engine's image limits (tests use smaller ones). */
|
|
53
|
+
readonly imageLimits?: ImageLimits;
|
|
45
54
|
/** Diagnostics for one request (no content), when a debug log is on. */
|
|
46
55
|
debug?(message: string): void;
|
|
47
56
|
readonly fetch?: typeof fetch;
|
|
@@ -78,5 +87,11 @@ export declare class AdaptiqAdapter extends LlmAdapter {
|
|
|
78
87
|
resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
79
88
|
prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
|
|
80
89
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
90
|
+
/**
|
|
91
|
+
* The request's images, read from the harness's stored copies, when the
|
|
92
|
+
* model's card says it reads images. Otherwise each image goes as a short
|
|
93
|
+
* note and the person is told.
|
|
94
|
+
*/
|
|
95
|
+
private images;
|
|
81
96
|
private generate;
|
|
82
97
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Images a person attached, as inline data the AdaptiqAI API accepts.
|
|
3
|
+
*
|
|
4
|
+
* The harness stores each attached image once, content-addressed (sha256), and
|
|
5
|
+
* hands the adapter a durable reference. The bytes sent are read from that
|
|
6
|
+
* stored copy, never from the file the person picked, so a history image is the
|
|
7
|
+
* same bytes on every turn (the engine dedupes by content hash) even when the
|
|
8
|
+
* original file has changed since.
|
|
9
|
+
*/
|
|
10
|
+
import type { ImageBlock, RequestMessage } from '@deepseek-ai/dsh-llm';
|
|
11
|
+
/** One image reference as the harness stores it. */
|
|
12
|
+
export type ImageRef = ImageBlock['attachment'];
|
|
13
|
+
/** Image media types the engine accepts. */
|
|
14
|
+
export type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
|
|
15
|
+
/** What the engine accepts per request. */
|
|
16
|
+
export interface ImageLimits {
|
|
17
|
+
/** Image parts in one request (history included). */
|
|
18
|
+
readonly maxImages: number;
|
|
19
|
+
/** Encoded bytes of one image. */
|
|
20
|
+
readonly maxImageBytes: number;
|
|
21
|
+
/** Width × height of one image; larger ones are scaled down before sending. */
|
|
22
|
+
readonly maxPixels: number;
|
|
23
|
+
}
|
|
24
|
+
/** The engine's limits behind api.adaptiqai.com (8 images, 20 MiB and 16.8 MP each); pixels kept a little under. */
|
|
25
|
+
export declare const ENGINE_IMAGE_LIMITS: ImageLimits;
|
|
26
|
+
/** The request version the harness's attachment service makes of one stored image. */
|
|
27
|
+
export interface RequestImageVersion {
|
|
28
|
+
readonly data: Uint8Array;
|
|
29
|
+
readonly mediaType: string;
|
|
30
|
+
readonly width: number;
|
|
31
|
+
readonly height: number;
|
|
32
|
+
}
|
|
33
|
+
/** The part of the harness's attachment service (`ctx.attachments`) the adapter reads images through. */
|
|
34
|
+
export interface ImageSource {
|
|
35
|
+
readImageRequest(ref: ImageRef, target: {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
maxBytes: number;
|
|
39
|
+
}, signal?: AbortSignal): Promise<RequestImageVersion>;
|
|
40
|
+
}
|
|
41
|
+
/** One image ready for the wire. */
|
|
42
|
+
export interface WireImage {
|
|
43
|
+
/** `data:<mime>;base64,...` */
|
|
44
|
+
readonly url: string;
|
|
45
|
+
readonly width: number;
|
|
46
|
+
readonly height: number;
|
|
47
|
+
}
|
|
48
|
+
/** An image the engine would refuse, said in plain words. */
|
|
49
|
+
export declare class ImageProblem extends Error {
|
|
50
|
+
readonly name = "ImageProblem";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The image format, read from the bytes themselves.
|
|
54
|
+
* @param data - encoded image bytes.
|
|
55
|
+
* @returns the media type, or null when the bytes are no PNG, JPEG, WebP or GIF.
|
|
56
|
+
*/
|
|
57
|
+
export declare function sniffImageType(data: Uint8Array): ImageMediaType | null;
|
|
58
|
+
/**
|
|
59
|
+
* The size to send one image at: its own, or scaled down (aspect kept) to fit the pixel limit.
|
|
60
|
+
* @param width - stored width.
|
|
61
|
+
* @param height - stored height.
|
|
62
|
+
* @param maxPixels - width × height limit.
|
|
63
|
+
* @returns target width and height.
|
|
64
|
+
*/
|
|
65
|
+
export declare function fitPixels(width: number, height: number, maxPixels: number): {
|
|
66
|
+
width: number;
|
|
67
|
+
height: number;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The images a request sends (not offloaded by the harness), one entry per occurrence, oldest first.
|
|
71
|
+
* @param messages - the request history.
|
|
72
|
+
* @returns image blocks in request order.
|
|
73
|
+
*/
|
|
74
|
+
export declare function sentImages(messages: readonly RequestMessage[]): ImageBlock[];
|
|
75
|
+
/**
|
|
76
|
+
* Whether any message of the request carries an image.
|
|
77
|
+
* @param messages - the request history.
|
|
78
|
+
* @returns true when one does.
|
|
79
|
+
*/
|
|
80
|
+
export declare function hasImages(messages: readonly RequestMessage[]): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Read every image the request sends from the harness's stored copies, check
|
|
83
|
+
* it against the engine's limits and encode it as a data URL.
|
|
84
|
+
* @param messages - the request history.
|
|
85
|
+
* @param source - the harness's attachment service.
|
|
86
|
+
* @param limits - the engine's limits.
|
|
87
|
+
* @param signal - cancellation.
|
|
88
|
+
* @returns attachment id → wire image.
|
|
89
|
+
* @throws ImageProblem when an image is not one the engine accepts.
|
|
90
|
+
*/
|
|
91
|
+
export declare function prepareImages(messages: readonly RequestMessage[], source: ImageSource, limits: ImageLimits, signal?: AbortSignal): Promise<Map<string, WireImage>>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
/** Where the entries of `minimumReleaseAgeExclude` sit in the file's lines. */
|
|
3
|
+
interface ExcludeList {
|
|
4
|
+
readonly lines: readonly string[];
|
|
5
|
+
/** The key's line, or -1 when the key is absent. */
|
|
6
|
+
readonly keyLine: number;
|
|
7
|
+
/** The key line rewritten for a block list, when it held an empty flow list (`[]`). */
|
|
8
|
+
readonly blockKey: string | null;
|
|
9
|
+
/** Line index and value of each entry. */
|
|
10
|
+
readonly entries: readonly {
|
|
11
|
+
readonly line: number;
|
|
12
|
+
readonly value: string;
|
|
13
|
+
}[];
|
|
14
|
+
readonly indent: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read the exclusion list out of pnpm-workspace.yaml.
|
|
18
|
+
* @param text - the file's contents.
|
|
19
|
+
* @returns where the list is, or null when the layout is not one this edits with certainty.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readExcludeList(text: string): ExcludeList | null;
|
|
22
|
+
/**
|
|
23
|
+
* The file with exactly `name@version` listed for this package: older entries
|
|
24
|
+
* of the same package are replaced, every other line stays as it is.
|
|
25
|
+
* @param text - pnpm-workspace.yaml as it is.
|
|
26
|
+
* @param name - the plugin's package name.
|
|
27
|
+
* @param version - the release about to be installed.
|
|
28
|
+
* @returns the new text (the same text when nothing needs to change), or null when it cannot be edited with certainty.
|
|
29
|
+
*/
|
|
30
|
+
export declare function excludeRelease(text: string, name: string, version: string): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* The profile directory pnpm installs plugins in: the same `profileContext`
|
|
33
|
+
* the plugin manager installs through. Environment variables are not used: a
|
|
34
|
+
* harness started from another profile's shell inherits that profile's.
|
|
35
|
+
* @param ctx - the plugin context.
|
|
36
|
+
* @returns the directory, or null outside a dsh profile.
|
|
37
|
+
*/
|
|
38
|
+
export declare function profileDir(ctx: Context): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the exclusion can be written: the file exists, reads with certainty and is writable.
|
|
41
|
+
* @param dir - the profile directory.
|
|
42
|
+
* @param name - the plugin's package name.
|
|
43
|
+
* @returns true when {@link writeReleaseExclusion} would succeed short of a race.
|
|
44
|
+
*/
|
|
45
|
+
export declare function canExcludeRelease(dir: string, name: string): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* List exactly `name@version` in the profile's `minimumReleaseAgeExclude`,
|
|
48
|
+
* under the profile's package lock (the one the plugin manager holds while it
|
|
49
|
+
* changes the profile), with an atomic replace.
|
|
50
|
+
* @param dir - the profile directory.
|
|
51
|
+
* @param name - the plugin's package name.
|
|
52
|
+
* @param version - the release about to be installed.
|
|
53
|
+
* @param waitMs - how long to wait for another package operation in the profile.
|
|
54
|
+
* @returns true when the file lists the version; false when it was left as it was.
|
|
55
|
+
*/
|
|
56
|
+
export declare function writeReleaseExclusion(dir: string, name: string, version: string, waitMs?: number): Promise<boolean>;
|
|
57
|
+
export {};
|
package/lib/types/host/wire.d.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Harness request → OpenAI Chat Completions body. Model-specific behaviour
|
|
3
|
-
* (thinking field and values, sampling defaults) comes only from the
|
|
4
|
-
* model's catalog entry.
|
|
5
|
-
*/
|
|
6
1
|
import type { GenerateOptions, RequestMessage } from '@deepseek-ai/dsh-llm';
|
|
7
2
|
import { type CatalogModel, type SessionRole, type UserTurn } from '@adaptiqai/core';
|
|
3
|
+
import type { ImageRef, WireImage } from './images.ts';
|
|
8
4
|
/** One Chat Completions message. */
|
|
9
5
|
export type WireMessage = {
|
|
10
6
|
role: 'system';
|
|
11
7
|
content: string;
|
|
12
8
|
} | {
|
|
13
9
|
role: 'user';
|
|
14
|
-
content: string;
|
|
10
|
+
content: string | WirePart[];
|
|
15
11
|
} | {
|
|
16
12
|
role: 'assistant';
|
|
17
13
|
content: string | null;
|
|
@@ -22,6 +18,25 @@ export type WireMessage = {
|
|
|
22
18
|
tool_call_id: string;
|
|
23
19
|
content: string;
|
|
24
20
|
};
|
|
21
|
+
/** One part of a user message that carries images, in the person's order. */
|
|
22
|
+
export type WirePart = {
|
|
23
|
+
type: 'text';
|
|
24
|
+
text: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'image_url';
|
|
27
|
+
image_url: {
|
|
28
|
+
url: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/** How the images of a request go on the wire. */
|
|
32
|
+
export interface WireImages {
|
|
33
|
+
/** Attachment id → inline image; null when the model reads text only (each image becomes a short note). */
|
|
34
|
+
readonly sent: ReadonlyMap<string, WireImage> | null;
|
|
35
|
+
/** Where the model's tools can read the harness's stored copy of an image. */
|
|
36
|
+
readonly access?: (ref: ImageRef) => {
|
|
37
|
+
readonlyPath: string;
|
|
38
|
+
} | undefined;
|
|
39
|
+
}
|
|
25
40
|
export interface WireToolCall {
|
|
26
41
|
id: string;
|
|
27
42
|
type: 'function';
|
|
@@ -40,6 +55,8 @@ export interface WireExtras {
|
|
|
40
55
|
readonly includeUsage?: boolean;
|
|
41
56
|
/** Referenced sessions attached to this conversation and ready, by session id → name. */
|
|
42
57
|
readonly recalled?: ReadonlyMap<string, string>;
|
|
58
|
+
/** The request's images; without it every image becomes a short note. */
|
|
59
|
+
readonly images?: WireImages;
|
|
43
60
|
}
|
|
44
61
|
/** One session another message mentioned with `@`, as the harness snapshot records it. */
|
|
45
62
|
export interface ReferencedSession {
|
|
@@ -94,9 +111,10 @@ export declare function userTurns(messages: readonly RequestMessage[]): UserTurn
|
|
|
94
111
|
* Convert the harness history to Chat Completions messages.
|
|
95
112
|
* @param options - the harness request.
|
|
96
113
|
* @param recalled - referenced sessions read from memory: their snapshots become one line.
|
|
114
|
+
* @param images - the request's images; without it each image becomes a short note.
|
|
97
115
|
* @returns the wire messages in order.
|
|
98
116
|
*/
|
|
99
|
-
export declare function wireMessages(options: Pick<GenerateOptions, 'messages' | 'system'>, recalled?: ReadonlyMap<string, string
|
|
117
|
+
export declare function wireMessages(options: Pick<GenerateOptions, 'messages' | 'system'>, recalled?: ReadonlyMap<string, string>, images?: WireImages): WireMessage[];
|
|
100
118
|
/**
|
|
101
119
|
* Build the request body for one call.
|
|
102
120
|
* @param options - the harness request.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptiqai/deepseek-harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "AdaptiqAI for DeepSeek Harness: models with 5M-token native memory and Knowledge. Sign up, get a model and work without leaving the harness",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh-plugin",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"react-dom": "~18.3.1",
|
|
91
91
|
"typescript": "~5.9.3",
|
|
92
92
|
"vitest": "~3.2.7",
|
|
93
|
-
"@adaptiqai/core": "0.2.
|
|
93
|
+
"@adaptiqai/core": "0.2.9"
|
|
94
94
|
},
|
|
95
95
|
"repository": {
|
|
96
96
|
"type": "git",
|