@deepseek-ai/dsh-attachment 0.1.1-rc.2 → 0.1.2-alpha.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.i18n.yaml +2 -2
- package/README.md +118 -7
- package/README.zh.md +120 -9
- package/lib/index.js +51 -2
- package/lib/types/index.d.ts +9 -1
- package/lib/types/index.js +12 -1
- package/lib/types/request-projection.d.ts +16 -0
- package/lib/types/request-projection.js +33 -0
- package/lib/types/types.d.ts +1 -1
- package/package.json +7 -7
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/attachment/attachment/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 01a5d6ee143ff53175dd7327cd6d419af61938a3
|
|
6
|
+
README.zh.md: 1a1d297297a6815ce5338391af0182e471f99000
|
package/README.md
CHANGED
|
@@ -1,16 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Durable image attachments for users and maintainers attaching, reusing, or debugging images in prompts and commands."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-attachment
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
You can attach images to prompts and commands, and the harness keeps provider-independent normalized versions durably: each source image is admitted and normalized before your message is processed, reappears in conversation history, and is projected to the selected model route in later turns of the same session. The shipped `dsh` composition enables this with no setup. Attached images survive restarts, while browser paths, provider URLs, local storage paths, and base64 never enter durable session events. Only raster formats (PNG, JPEG, WebP, GIF) are accepted, and unsent composer drafts stay in the browser until you submit. Stored images are never deleted automatically, and non-image files, audio, and video are not supported yet.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Image attachments work end to end: attach an image to a prompt or a command, and it is saved, shown in history, and sent to the model without any further action from you. In the default `dsh` composition everything is already wired; when you compose your own setup, one plugin enables the capability.
|
|
29
|
+
|
|
30
|
+
### Attach images to a prompt
|
|
31
|
+
|
|
32
|
+
Attach one or more images to a user prompt in the client UI. Each source is checked, normalized to a provider-independent 8-bit sRGB/sRGBA raster, and saved before your message is processed; if any image is refused, the whole message fails and nothing is published. Supported source formats are PNG, JPEG, WebP, and GIF; a deployment controls source limits separately from normalized-storage and route-specific request limits. The one plugin below enables durable image attachments (the shipped base composition already mounts it):
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- name: '@deepseek-ai/dsh-attachment-local'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Pass images to commands
|
|
39
|
+
|
|
40
|
+
Commands that accept image input receive attached images the same way. If a command does not accept images, the harness refuses with an error message instead of silently dropping them.
|
|
41
|
+
|
|
42
|
+
### Reuse images across the session
|
|
43
|
+
|
|
44
|
+
Saved normalized images stay in conversation history and are projected into deterministic, route-sized request versions in later turns; after a restart, a resumed session shows and reuses the same images. When the current execution filesystem maps the stored host object, the request descriptor also carries a read-only process path that the model can inspect. When history or a request version is read back, the stored bytes are checked against what was recorded, so a missing, corrupted, or swapped image surfaces as an error rather than wrong bytes.
|
|
45
|
+
|
|
46
|
+
### What can go wrong
|
|
47
|
+
|
|
48
|
+
An image can be refused when you attach it — unsupported format, over the size, pixel, or dimension limits, or bytes that do not match their declared type — and the message then fails as a whole. Later, a history read can fail if the stored image was deleted or corrupted on disk. Failures carry stable codes so the client and protocol adapters can explain them in their own words.
|
|
49
|
+
|
|
50
|
+
-----
|
|
51
|
+
|
|
52
|
+
<a id="understand-the-implementation"></a>
|
|
53
|
+
## Understand the implementation
|
|
54
|
+
|
|
55
|
+
<details>
|
|
56
|
+
<summary>Implementation internals — click to expand</summary>
|
|
57
|
+
|
|
58
|
+
This section explains the design decisions behind the seam and the service operations that realize the user-visible behavior; observable behavior is fully covered in [Use this package](#use-this-package).
|
|
59
|
+
|
|
60
|
+
### Design decisions
|
|
6
61
|
|
|
7
|
-
|
|
62
|
+
- **Normalize and persist before event.** Every source is prepared and verified before the batch publishes in order, so the session log never references a partial or failed normalization.
|
|
63
|
+
- **Immutable and retention-neutral.** Objects are immutable once published; resumed and forked sessions may share them, so reference-aware garbage collection is deferred rather than tied to any one session's deletion.
|
|
64
|
+
- **Verify on read.** Reads check bytes and metadata against the logged reference before returning them, and request projections fully decode cached bytes, so a missing, corrupted, or swapped object fails closed.
|
|
65
|
+
- **Role-neutral image blocks.** The `ImageBlock` content block in `dsh-llm` carries an `ImageAttachmentRef`; provider adapters resolve it into deterministic request versions with explicit pixel and byte budgets, while execution filesystems may map the immutable host object to a model-readable process path.
|
|
66
|
+
- **Error routing by code.** `AttachmentError` re-implements the `HarnessError` shape instead of extending it because the base lives in `dsh-llm`, which depends on this package; consumers route on `code`, never on the prototype chain.
|
|
8
67
|
|
|
9
|
-
|
|
68
|
+
### Service operations
|
|
10
69
|
|
|
70
|
+
The service family runs one admission-and-storage flow: every entry point enforces source batch limits and canonical base64, prepares provider-independent normalized attachments before publishing any member, and commits them durably in input order without partial results. `readImageRequest` derives deterministic route-sized variants whose identity includes the attachment id, transform version, pixel and byte budgets, and encoder settings. The pure `requestImageDimensions` export computes each projection's aspect-preserving dimensions from a total-pixel budget, so providers and request pricing share one geometry. `imageHostPath` exposes an implementation-owned host location only to trusted same-process consumers that need execution-world mapping. Callers compose ordered batches while the implementation owns compression concurrency, caching, and singleflight. Reads and projections preserve caller cancellation. Failures carry stable machine-readable codes, and the caller-correctable admission subset is recognizable at runtime so each protocol adapter maps its own vocabulary; the exact per-operation contracts live in [`src/index.ts`](src/index.ts) and [`src/error.ts`](src/error.ts).
|
|
71
|
+
|
|
72
|
+
### Source map
|
|
73
|
+
|
|
74
|
+
| File | Role |
|
|
75
|
+
|---|---|
|
|
76
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: abstract `AttachmentStore` service and re-exports |
|
|
77
|
+
| [`src/types.ts`](src/types.ts) | Durable vocabulary: references, limits, upload and store payloads |
|
|
78
|
+
| [`src/admission.ts`](src/admission.ts) | `admitEncodedImages`: canonical-base64 enforcement, then `saveImages` delegation |
|
|
79
|
+
| [`src/error.ts`](src/error.ts) | `AttachmentError` class and the `isImageAdmissionError` runtime subset |
|
|
80
|
+
| [`src/brand.ts`](src/brand.ts) | `AttachmentId` branded opaque identifier |
|
|
81
|
+
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; implementations enforce immutable-store checks) |
|
|
82
|
+
|
|
83
|
+
</details>
|
|
84
|
+
|
|
85
|
+
-----
|
|
86
|
+
|
|
87
|
+
<a id="further-exploration"></a>
|
|
88
|
+
## Further Exploration
|
|
89
|
+
|
|
90
|
+
For the full service contract and payload types, read the subsystem reference; for the storage that backs this capability, read the local backend.
|
|
91
|
+
|
|
92
|
+
- [Attachment subsystem reference](../../../docs/subsystems/attachment.md) — service contract, payload types, and the `ctx.attachments` cordis surface.
|
|
93
|
+
- [Local filesystem backend](../attachment-local/README.md) — where your attached images are stored on this machine.
|
|
94
|
+
- [Capability seams](../../../docs/capability-seams.md) — how this capability family is split into roles.
|
|
95
|
+
|
|
96
|
+
-----
|
|
97
|
+
|
|
98
|
+
<a id="model-experience"></a>
|
|
11
99
|
## Model Experience
|
|
12
100
|
|
|
13
|
-
Indirectly, through the
|
|
101
|
+
Indirectly, through the provider adapter, which resolves each durable reference into an exact request version and sends its stable attachment id and actual dimensions beside the image. When the execution filesystem maps the stored object, the descriptor also includes a read-only process path and a matching extension for a writable copy.
|
|
14
102
|
|
|
15
103
|
#### KV Cache effect
|
|
16
104
|
|
|
@@ -18,6 +106,29 @@ Adding an image changes the provider request and therefore invalidates the affec
|
|
|
18
106
|
|
|
19
107
|
## Known Limitations and Deferred Work
|
|
20
108
|
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
109
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
These limits describe what image attachments can and cannot do; they are current package constraints, not a task backlog.
|
|
113
|
+
|
|
114
|
+
- **Raster images only** — PNG, JPEG, WebP, and GIF are accepted; generic files, audio, and video are not supported yet.
|
|
115
|
+
- **Images are never deleted** — stored images are retained indefinitely; nothing removes them automatically.
|
|
116
|
+
- **Unsent drafts are not saved** — a composer draft stays in the browser until you submit the message.
|
|
117
|
+
|
|
118
|
+
<a id="dev-note"></a>
|
|
119
|
+
### Dev Note
|
|
120
|
+
|
|
121
|
+
<details>
|
|
122
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
123
|
+
|
|
124
|
+
This Dev Note is working context for maintainers: undecided directions and open questions. It is explicitly non-authoritative — shipped behavior and limits live in the sections above and the package code.
|
|
125
|
+
|
|
126
|
+
#### Future: reference-aware garbage collection
|
|
127
|
+
|
|
128
|
+
Resumed and forked sessions may share immutable objects, so any retention policy needs a reference model that accounts for session lineage before objects can be collected. No decision is recorded yet; the local backend currently retains everything.
|
|
129
|
+
|
|
130
|
+
#### Future: non-image attachments and assistant-side output
|
|
131
|
+
|
|
132
|
+
Generic files, audio, and video would need separate lifecycle and provider contracts, and the role-neutral `ImageBlock` leaves assistant-side image output as forward compatibility — current production adapters declare text-only output, so only user content carries images. Both directions are undecided.
|
|
133
|
+
|
|
134
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,23 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "持久图片附件,供用户与维护者在提示词与命令中附加、复用或排查图片。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-attachment
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
你可以把图片附加到提示词和命令中,harness 会持久保存提供方无关的规范化版本:每张源图都会在你的消息被处理前准入并规范化,重新出现在对话历史中,并在同一会话的后续轮次投影到所选模型路由。随附的 `dsh` 组合无需任何配置即可支持这一点。已附加的图片在重启后依然存在,而浏览器路径、提供方 URL、本地存储路径与 base64 绝不会进入持久会话事件。只接受光栅格式(PNG、JPEG、WebP、GIF),未发送的输入区草稿在提交前仍留在浏览器中。已存储的图片永远不会被自动删除,通用文件、音频和视频暂不支持。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
图片附件端到端可用:把图片附加到提示词或命令,它会自动保存、显示在历史中并发送给模型,无需你再做任何操作。在默认 `dsh` 组合中一切都已接好;自行组合时,一个插件即可启用该能力。
|
|
29
|
+
|
|
30
|
+
### 在提示词中附加图片
|
|
31
|
+
|
|
32
|
+
在客户端 UI 中向用户提示词附加一张或多张图片。每个源图都会在你的消息被处理前接受检查、规范化为提供方无关的 8-bit sRGB/sRGBA 光栅并保存;如果任何一张图片被拒绝,整条消息都会失败且不会发布任何内容。支持的源格式为 PNG、JPEG、WebP 与 GIF;部署方分别控制源图限制、规范化存储限制与路由专用请求限制。下面这一个插件即可启用持久图片附件(随附的 base 组合已经挂载它):
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- name: '@deepseek-ai/dsh-attachment-local'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 把图片传给命令
|
|
39
|
+
|
|
40
|
+
接受图片输入的命令以相同方式接收附加图片。如果某个命令不接受图片,harness 会以错误消息拒绝,而不是静默丢弃。
|
|
41
|
+
|
|
42
|
+
### 在整个会话中复用图片
|
|
43
|
+
|
|
44
|
+
已保存的规范化图片会保留在对话历史中,并在后续轮次投影为确定性的路由尺寸请求版本;重启后,恢复的会话会显示并复用相同的图片。当前执行文件系统可以映射已存宿主对象时,请求描述符还会携带模型可检查的只读进程路径。回读历史或请求版本时,已存储的字节会与记录的内容比对,因此缺失、损坏或被替换的图片会以错误形式呈现,而不是错误的字节。
|
|
45
|
+
|
|
46
|
+
### 可能出什么问题
|
|
47
|
+
|
|
48
|
+
附加图片时可能被拒绝——格式不受支持、超出大小、像素或尺寸限制,或者字节与声明类型不符——此时整条消息失败。之后,如果磁盘上的图片被删除或损坏,历史读取也可能失败。失败带有稳定错误码,客户端与协议适配器可以用自己的措辞解释它们。
|
|
49
|
+
|
|
50
|
+
-----
|
|
51
|
+
|
|
52
|
+
<a id="understand-the-implementation"></a>
|
|
53
|
+
## 理解实现
|
|
54
|
+
|
|
55
|
+
<details>
|
|
56
|
+
<summary>实现细节——点击展开</summary>
|
|
57
|
+
|
|
58
|
+
本节解释 seam 背后的设计决策,以及实现用户可见行为的服务操作;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
59
|
+
|
|
60
|
+
### 设计决策
|
|
6
61
|
|
|
7
|
-
|
|
62
|
+
- **事件前完成规范化与持久化。** 每个源图都会在批次按序发布前完成准备与校验,因此会话日志绝不会引用部分完成或规范化失败的对象。
|
|
63
|
+
- **不可变且保留策略中立。** 对象一经发布即不可变;恢复和 fork 后的会话可能共享它们,因此引用感知的垃圾回收被推迟,而不是与任何单个会话的删除绑定。
|
|
64
|
+
- **读取时校验。** 读取在返回前把字节和元数据与记录的引用比对,请求投影还会完整解码缓存字节,因此缺失、损坏或被替换的对象都会失败关闭。
|
|
65
|
+
- **角色无关的图片块。** `dsh-llm` 中的 `ImageBlock` 内容块携带 `ImageAttachmentRef`;提供方适配器以显式像素与字节预算把引用解析为确定性请求版本,执行文件系统则可以把不可变宿主对象映射为模型可读的进程路径。
|
|
66
|
+
- **按错误码路由。** `AttachmentError` 重新实现 `HarnessError` 的结构而不是继承它,因为基类位于 `dsh-llm`,而后者依赖本包;消费方按 `code` 路由,绝不依赖原型链。
|
|
8
67
|
|
|
9
|
-
|
|
68
|
+
### 服务操作
|
|
10
69
|
|
|
70
|
+
服务族运行同一条准入与存储流程:每个入口都强制执行源批次限制与规范 base64,在发布任何成员前准备提供方无关的规范化附件,再按输入顺序持久提交而不产生部分结果。`readImageRequest` 派生确定性的路由尺寸变体,其身份包含附件 id、变换版本、像素与字节预算及编码参数。纯函数导出 `requestImageDimensions` 会按总像素预算计算每个投影保持宽高比的尺寸,使提供方与请求定价共享同一套几何计算。`imageHostPath` 只向需要执行世界映射的受信任同进程消费方暴露实现拥有的宿主位置。调用方组合有序批次,而实现拥有压缩并发、缓存与 singleflight。读取和投影保留调用方的取消语义。失败带有稳定且机器可读的错误码,运行时即可识别可由调用方修正的准入子集,让每个协议适配器映射自己的词汇;各操作的确切约定见 [`src/index.ts`](src/index.ts) 与 [`src/error.ts`](src/error.ts)。
|
|
71
|
+
|
|
72
|
+
### 源码地图
|
|
73
|
+
|
|
74
|
+
| 文件 | 职责 |
|
|
75
|
+
|---|---|
|
|
76
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:抽象 `AttachmentStore` 服务与再导出 |
|
|
77
|
+
| [`src/types.ts`](src/types.ts) | 持久词汇:引用、限额、上传与存储载荷 |
|
|
78
|
+
| [`src/admission.ts`](src/admission.ts) | `admitEncodedImages`:规范 base64 强制,随后委托 `saveImages` |
|
|
79
|
+
| [`src/error.ts`](src/error.ts) | `AttachmentError` 类与 `isImageAdmissionError` 运行时子集 |
|
|
80
|
+
| [`src/brand.ts`](src/brand.ts) | `AttachmentId` 带类型标记的不透明标识符 |
|
|
81
|
+
| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;实现负责强制不可变存储检查) |
|
|
82
|
+
|
|
83
|
+
</details>
|
|
84
|
+
|
|
85
|
+
-----
|
|
86
|
+
|
|
87
|
+
<a id="further-exploration"></a>
|
|
88
|
+
## 进一步探索
|
|
89
|
+
|
|
90
|
+
完整的服务约定与载荷类型请看子系统参考;支撑这一能力的存储请看本地后端。
|
|
91
|
+
|
|
92
|
+
- [附件子系统参考](../../../docs/subsystems/attachment.zh.md)——服务约定、载荷类型与 `ctx.attachments` 的 cordis 接口面。
|
|
93
|
+
- [本地文件系统后端](../attachment-local/README.zh.md)——你的附加图片在本机上的存储位置。
|
|
94
|
+
- [能力 seam](../../../docs/capability-seams.zh.md)——本能力家族如何拆分为多个角色。
|
|
95
|
+
|
|
96
|
+
-----
|
|
97
|
+
|
|
98
|
+
<a id="model-experience"></a>
|
|
11
99
|
## 模型体验
|
|
12
100
|
|
|
13
|
-
|
|
101
|
+
该包通过提供方适配器间接影响模型;适配器会把每个持久引用解析为确切请求版本,并在图片旁发送稳定附件 id 与实际尺寸。执行文件系统可以映射已存对象时,描述符还会包含只读进程路径,以及可写副本使用的匹配扩展名。
|
|
14
102
|
|
|
15
|
-
#### KV
|
|
103
|
+
#### KV Cache 影响
|
|
16
104
|
|
|
17
105
|
添加图片会改变提供方请求,因此会使受影响的请求后缀失效。
|
|
18
106
|
|
|
19
|
-
##
|
|
107
|
+
## 已知限制与延期工作
|
|
108
|
+
|
|
109
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
这些限制描述了图片附件能做什么、不能做什么;它们是当前包约束,而非任务积压。
|
|
113
|
+
|
|
114
|
+
- **仅支持光栅图片**——接受 PNG、JPEG、WebP 与 GIF;通用文件、音频和视频暂不支持。
|
|
115
|
+
- **图片永远不会被删除**——已存储的图片无限期保留;没有任何机制自动移除它们。
|
|
116
|
+
- **未发送的草稿不会保存**——输入区草稿在提交消息前一直留在浏览器中。
|
|
117
|
+
|
|
118
|
+
<a id="dev-note"></a>
|
|
119
|
+
### 开发备注
|
|
120
|
+
|
|
121
|
+
<details>
|
|
122
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
123
|
+
|
|
124
|
+
本开发备注是维护者的工作上下文:尚未决定的探索方向与开放问题。它明确不具权威性——已交付的行为与限制以上文和包代码为准。
|
|
125
|
+
|
|
126
|
+
#### 未来:引用感知的垃圾回收
|
|
127
|
+
|
|
128
|
+
恢复和 fork 后的会话可能共享不可变对象,因此任何保留策略都需要一个能考虑会话血缘的引用模型,之后才能回收对象。目前尚未记录任何决定;本地后端当前保留一切。
|
|
129
|
+
|
|
130
|
+
#### 未来:非图片附件与助手侧输出
|
|
131
|
+
|
|
132
|
+
通用文件、音频与视频需要单独的生命周期与提供方契约;角色无关的 `ImageBlock` 也把助手侧图片输出留作前瞻兼容——当前生产适配器声明只输出文本,因此只有用户内容携带图片。两个方向都尚未决定。
|
|
20
133
|
|
|
21
|
-
|
|
22
|
-
- 保留策略与垃圾回收尚未实现,因为恢复和 fork 后的会话可能共享不可变对象。
|
|
23
|
-
- 通用文件、音频、视频和持久的未发送草稿需要单独的生命周期与提供方契约。
|
|
134
|
+
</details>
|
package/lib/index.js
CHANGED
|
@@ -94,6 +94,48 @@ async function admitEncodedImages(attachments, images) {
|
|
|
94
94
|
return attachments.saveImages(images.map(saveInput));
|
|
95
95
|
}
|
|
96
96
|
//#endregion
|
|
97
|
+
//#region lib/types/request-projection.js
|
|
98
|
+
/**
|
|
99
|
+
* Pure request-projection geometry shared by attachment providers and
|
|
100
|
+
* provider-side request pricing. @module @deepseek-ai/dsh-attachment/request-projection
|
|
101
|
+
*/
|
|
102
|
+
/**
|
|
103
|
+
* Compute aspect-preserving integer dimensions within a hard total-pixel budget.
|
|
104
|
+
* @param width - positive source width.
|
|
105
|
+
* @param height - positive source height.
|
|
106
|
+
* @param maxPixels - positive width-times-height cap.
|
|
107
|
+
* @returns inward-rounded dimensions; small images are not enlarged.
|
|
108
|
+
*/
|
|
109
|
+
function requestImageDimensions(width, height, maxPixels) {
|
|
110
|
+
const scale = Math.min(1, Math.sqrt(maxPixels / (width * height)));
|
|
111
|
+
if (scale === 1) return {
|
|
112
|
+
width,
|
|
113
|
+
height
|
|
114
|
+
};
|
|
115
|
+
if (width >= height) {
|
|
116
|
+
let projectedWidth = Math.max(1, Math.floor(width * scale));
|
|
117
|
+
let projectedHeight = Math.max(1, Math.round(projectedWidth * height / width));
|
|
118
|
+
while (projectedWidth * projectedHeight > maxPixels && projectedWidth > 1) {
|
|
119
|
+
projectedWidth -= 1;
|
|
120
|
+
projectedHeight = Math.max(1, Math.round(projectedWidth * height / width));
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
width: projectedWidth,
|
|
124
|
+
height: projectedHeight
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
let projectedHeight = Math.max(1, Math.floor(height * scale));
|
|
128
|
+
let projectedWidth = Math.max(1, Math.round(projectedHeight * width / height));
|
|
129
|
+
while (projectedWidth * projectedHeight > maxPixels && projectedHeight > 1) {
|
|
130
|
+
projectedHeight -= 1;
|
|
131
|
+
projectedWidth = Math.max(1, Math.round(projectedHeight * width / height));
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
width: projectedWidth,
|
|
135
|
+
height: projectedHeight
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
97
139
|
//#region lib/types/index.js
|
|
98
140
|
/** Durable attachment storage seam (`ctx.attachments`). @module @deepseek-ai/dsh-attachment */
|
|
99
141
|
/** Immutable binary attachment service. Implementations validate bytes before publishing a reference. */
|
|
@@ -128,9 +170,16 @@ var AttachmentStore = class extends Service {
|
|
|
128
170
|
return refs;
|
|
129
171
|
}
|
|
130
172
|
/**
|
|
173
|
+
* Locate the provider-owned normalized object in the harness host filesystem.
|
|
174
|
+
* @param ref - durable normalized attachment reference.
|
|
175
|
+
* @returns an absolute host path, or undefined when this backend is not host-file-backed.
|
|
176
|
+
* @throws an AttachmentError when the durable reference is invalid.
|
|
177
|
+
*/
|
|
178
|
+
imageHostPath(ref) {}
|
|
179
|
+
/**
|
|
131
180
|
* Generate or read one deterministic model-request version from the stored normalized image.
|
|
132
181
|
* @param ref - durable provider-independent normalized attachment reference.
|
|
133
|
-
* @param policy - exact route pixel and encoded-byte
|
|
182
|
+
* @param policy - exact route pixel budget and encoded-byte target; a target no ladder quality meets yields the smallest ladder output.
|
|
134
183
|
* @param signal - optional cancellation.
|
|
135
184
|
* @returns request bytes and the cache/upload identity covering every transform input.
|
|
136
185
|
*/
|
|
@@ -140,4 +189,4 @@ var AttachmentStore = class extends Service {
|
|
|
140
189
|
}
|
|
141
190
|
};
|
|
142
191
|
//#endregion
|
|
143
|
-
export { AttachmentError, AttachmentId, AttachmentStore, AttachmentStore as default, ImageVariantId, admitEncodedImages, isImageAdmissionError };
|
|
192
|
+
export { AttachmentError, AttachmentId, AttachmentStore, AttachmentStore as default, ImageVariantId, admitEncodedImages, isImageAdmissionError, requestImageDimensions };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { AttachmentId, ImageVariantId } from './brand.ts';
|
|
|
5
5
|
export { AttachmentError, isImageAdmissionError } from './error.ts';
|
|
6
6
|
export type { AttachmentErrorCode, ImageAdmissionErrorCode } from './error.ts';
|
|
7
7
|
export { admitEncodedImages } from './admission.ts';
|
|
8
|
+
export { requestImageDimensions } from './request-projection.ts';
|
|
8
9
|
export type { AttachmentId as AttachmentIdType, EncodedImageAttachment, ImageAttachmentLimits, ImageAttachmentRef, ImageRequestPolicy, ImageMediaType, RequestImageAttachment, SaveImageAttachment, StoredImageAttachment, } from './types.ts';
|
|
9
10
|
declare module '@deepseek-ai/cordis' {
|
|
10
11
|
interface Context {
|
|
@@ -55,10 +56,17 @@ export declare abstract class AttachmentStore extends Service {
|
|
|
55
56
|
* @throws the signal reason when aborted, or a storage error when verification fails.
|
|
56
57
|
*/
|
|
57
58
|
abstract readImage(ref: ImageAttachmentRef, signal?: AbortSignal): Promise<StoredImageAttachment>;
|
|
59
|
+
/**
|
|
60
|
+
* Locate the provider-owned normalized object in the harness host filesystem.
|
|
61
|
+
* @param ref - durable normalized attachment reference.
|
|
62
|
+
* @returns an absolute host path, or undefined when this backend is not host-file-backed.
|
|
63
|
+
* @throws an AttachmentError when the durable reference is invalid.
|
|
64
|
+
*/
|
|
65
|
+
imageHostPath(ref: ImageAttachmentRef): string | undefined;
|
|
58
66
|
/**
|
|
59
67
|
* Generate or read one deterministic model-request version from the stored normalized image.
|
|
60
68
|
* @param ref - durable provider-independent normalized attachment reference.
|
|
61
|
-
* @param policy - exact route pixel and encoded-byte
|
|
69
|
+
* @param policy - exact route pixel budget and encoded-byte target; a target no ladder quality meets yields the smallest ladder output.
|
|
62
70
|
* @param signal - optional cancellation.
|
|
63
71
|
* @returns request bytes and the cache/upload identity covering every transform input.
|
|
64
72
|
*/
|
package/lib/types/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { AttachmentError } from "./error.js";
|
|
|
4
4
|
export { AttachmentId, ImageVariantId } from "./brand.js";
|
|
5
5
|
export { AttachmentError, isImageAdmissionError } from "./error.js";
|
|
6
6
|
export { admitEncodedImages } from "./admission.js";
|
|
7
|
+
export { requestImageDimensions } from "./request-projection.js";
|
|
7
8
|
/** Immutable binary attachment service. Implementations validate bytes before publishing a reference. */
|
|
8
9
|
export class AttachmentStore extends Service {
|
|
9
10
|
constructor(ctx) {
|
|
@@ -46,10 +47,20 @@ export class AttachmentStore extends Service {
|
|
|
46
47
|
refs.push(await this.saveImage(input));
|
|
47
48
|
return refs;
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Locate the provider-owned normalized object in the harness host filesystem.
|
|
52
|
+
* @param ref - durable normalized attachment reference.
|
|
53
|
+
* @returns an absolute host path, or undefined when this backend is not host-file-backed.
|
|
54
|
+
* @throws an AttachmentError when the durable reference is invalid.
|
|
55
|
+
*/
|
|
56
|
+
imageHostPath(ref) {
|
|
57
|
+
void ref;
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
49
60
|
/**
|
|
50
61
|
* Generate or read one deterministic model-request version from the stored normalized image.
|
|
51
62
|
* @param ref - durable provider-independent normalized attachment reference.
|
|
52
|
-
* @param policy - exact route pixel and encoded-byte
|
|
63
|
+
* @param policy - exact route pixel budget and encoded-byte target; a target no ladder quality meets yields the smallest ladder output.
|
|
53
64
|
* @param signal - optional cancellation.
|
|
54
65
|
* @returns request bytes and the cache/upload identity covering every transform input.
|
|
55
66
|
*/
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure request-projection geometry shared by attachment providers and
|
|
3
|
+
* provider-side request pricing. @module @deepseek-ai/dsh-attachment/request-projection
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Compute aspect-preserving integer dimensions within a hard total-pixel budget.
|
|
7
|
+
* @param width - positive source width.
|
|
8
|
+
* @param height - positive source height.
|
|
9
|
+
* @param maxPixels - positive width-times-height cap.
|
|
10
|
+
* @returns inward-rounded dimensions; small images are not enlarged.
|
|
11
|
+
*/
|
|
12
|
+
export declare function requestImageDimensions(width: number, height: number, maxPixels: number): {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=request-projection.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure request-projection geometry shared by attachment providers and
|
|
3
|
+
* provider-side request pricing. @module @deepseek-ai/dsh-attachment/request-projection
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Compute aspect-preserving integer dimensions within a hard total-pixel budget.
|
|
7
|
+
* @param width - positive source width.
|
|
8
|
+
* @param height - positive source height.
|
|
9
|
+
* @param maxPixels - positive width-times-height cap.
|
|
10
|
+
* @returns inward-rounded dimensions; small images are not enlarged.
|
|
11
|
+
*/
|
|
12
|
+
export function requestImageDimensions(width, height, maxPixels) {
|
|
13
|
+
const scale = Math.min(1, Math.sqrt(maxPixels / (width * height)));
|
|
14
|
+
if (scale === 1)
|
|
15
|
+
return { width, height };
|
|
16
|
+
if (width >= height) {
|
|
17
|
+
let projectedWidth = Math.max(1, Math.floor(width * scale));
|
|
18
|
+
let projectedHeight = Math.max(1, Math.round(projectedWidth * height / width));
|
|
19
|
+
while (projectedWidth * projectedHeight > maxPixels && projectedWidth > 1) {
|
|
20
|
+
projectedWidth -= 1;
|
|
21
|
+
projectedHeight = Math.max(1, Math.round(projectedWidth * height / width));
|
|
22
|
+
}
|
|
23
|
+
return { width: projectedWidth, height: projectedHeight };
|
|
24
|
+
}
|
|
25
|
+
let projectedHeight = Math.max(1, Math.floor(height * scale));
|
|
26
|
+
let projectedWidth = Math.max(1, Math.round(projectedHeight * width / height));
|
|
27
|
+
while (projectedWidth * projectedHeight > maxPixels && projectedHeight > 1) {
|
|
28
|
+
projectedHeight -= 1;
|
|
29
|
+
projectedWidth = Math.max(1, Math.round(projectedHeight * width / height));
|
|
30
|
+
}
|
|
31
|
+
return { width: projectedWidth, height: projectedHeight };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=request-projection.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface StoredImageAttachment {
|
|
|
62
62
|
export interface ImageRequestPolicy {
|
|
63
63
|
/** Maximum width multiplied by height after aspect-preserving projection. */
|
|
64
64
|
maxPixels: number;
|
|
65
|
-
/** Encoded-byte
|
|
65
|
+
/** Encoded-byte target before base64 expansion or Files API upload; the smallest quality-ladder output is kept when no quality fits. */
|
|
66
66
|
maxBytes: number;
|
|
67
67
|
}
|
|
68
68
|
/** Cached request version derived from one provider-independent normalized attachment. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-attachment",
|
|
3
3
|
"description": "Durable immutable attachment storage seam for the DeepSeek Harness",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
],
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@deepseek-ai/
|
|
41
|
-
"@deepseek-ai/dsh-
|
|
42
|
-
"@deepseek-ai/
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
41
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.2",
|
|
42
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@deepseek-ai/dsh-brand": "^0.1.
|
|
46
|
-
"@deepseek-ai/
|
|
47
|
-
"@deepseek-ai/
|
|
45
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.2",
|
|
46
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
47
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
|
|
48
48
|
}
|
|
49
49
|
}
|