@qilin-agent/attachment 0.0.0-stage → 3.1.4
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/LICENSE +21 -0
- package/README.i18n.yaml +67 -0
- package/README.md +137 -2
- package/README.zh.md +138 -0
- package/lib/index.js +344 -0
- package/lib/types/admission.d.ts +26 -0
- package/lib/types/admission.js +52 -0
- package/lib/types/brand.d.ts +19 -0
- package/lib/types/brand.js +18 -0
- package/lib/types/error.d.ts +42 -0
- package/lib/types/error.js +72 -0
- package/lib/types/index.d.ts +132 -0
- package/lib/types/index.js +162 -0
- package/lib/types/request-projection.d.ts +28 -0
- package/lib/types/request-projection.js +49 -0
- package/lib/types/types.d.ts +155 -0
- package/lib/types/types.js +3 -0
- package/package.json +42 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kkutysllb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/attachment/attachment/README.md
|
|
5
|
+
/:
|
|
6
|
+
en: c8448ea82a8ce005
|
|
7
|
+
zh: 24444a57de8c007c
|
|
8
|
+
/qilin-agent-attachment:
|
|
9
|
+
en: f3786fd6eb58df60
|
|
10
|
+
zh: 884fab58b2f31edb
|
|
11
|
+
/qilin-agent-attachment/summary:
|
|
12
|
+
en: 49fad86021c62382
|
|
13
|
+
zh: 3d197d5caddd20b5
|
|
14
|
+
/qilin-agent-attachment/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: 09388d293f9be9cb
|
|
17
|
+
/qilin-agent-attachment/use-this-package:
|
|
18
|
+
en: e40629045ebd4da2
|
|
19
|
+
zh: d322d432d569fe26
|
|
20
|
+
/qilin-agent-attachment/use-this-package/attach-images-to-a-prompt:
|
|
21
|
+
en: b440422892839de9
|
|
22
|
+
zh: 0290b366b5a4ef88
|
|
23
|
+
/qilin-agent-attachment/use-this-package/attach-any-other-file-to-a-prompt:
|
|
24
|
+
en: d5019c6ae76c0bcf
|
|
25
|
+
zh: b7aeaf7e6920ff10
|
|
26
|
+
/qilin-agent-attachment/use-this-package/pass-attachments-to-commands:
|
|
27
|
+
en: 8a0fa64e84aa4450
|
|
28
|
+
zh: ed2ac44ef20250d4
|
|
29
|
+
/qilin-agent-attachment/use-this-package/reuse-images-across-the-session:
|
|
30
|
+
en: c99e4e088be92c4f
|
|
31
|
+
zh: 34997ecb9a420d08
|
|
32
|
+
/qilin-agent-attachment/use-this-package/what-can-go-wrong:
|
|
33
|
+
en: b35e2f7f17269ae1
|
|
34
|
+
zh: 57cf0ef9fee6c7e1
|
|
35
|
+
/qilin-agent-attachment/understand-the-implementation:
|
|
36
|
+
en: 25d9c7d6c0899d23
|
|
37
|
+
zh: ad8380d14810ff1c
|
|
38
|
+
/qilin-agent-attachment/understand-the-implementation/design-decisions:
|
|
39
|
+
en: 3e331752249fb41a
|
|
40
|
+
zh: 8043a3d12fc777f7
|
|
41
|
+
/qilin-agent-attachment/understand-the-implementation/service-operations:
|
|
42
|
+
en: 4a35cdba790c3b8b
|
|
43
|
+
zh: 08d4a18bbad4b182
|
|
44
|
+
/qilin-agent-attachment/understand-the-implementation/source-map:
|
|
45
|
+
en: 4fb4c96184c38169
|
|
46
|
+
zh: 015b06e4bfff4fcd
|
|
47
|
+
/qilin-agent-attachment/further-exploration:
|
|
48
|
+
en: 858ae208bf596a29
|
|
49
|
+
zh: 7e5f18fbdf6e1c5b
|
|
50
|
+
/qilin-agent-attachment/model-experience:
|
|
51
|
+
en: 3a9fc2cc8896a2db
|
|
52
|
+
zh: 256ffa52315cb597
|
|
53
|
+
/qilin-agent-attachment/model-experience/kv-cache-effect:
|
|
54
|
+
en: 2fcaf6d01d42da7c
|
|
55
|
+
zh: 81e61690b955dd18
|
|
56
|
+
/qilin-agent-attachment/known-limitations-and-deferred-work:
|
|
57
|
+
en: d1ee04ac73471377
|
|
58
|
+
zh: 1abf40510951d5c2
|
|
59
|
+
/qilin-agent-attachment/known-limitations-and-deferred-work/dev-note:
|
|
60
|
+
en: d56846da29ad869c
|
|
61
|
+
zh: 41e655e01351fd1d
|
|
62
|
+
/qilin-agent-attachment/known-limitations-and-deferred-work/dev-note/future-reference-aware-garbage-collection:
|
|
63
|
+
en: 20c22bbf4263d41e
|
|
64
|
+
zh: f85699e18a0e5f43
|
|
65
|
+
/qilin-agent-attachment/known-limitations-and-deferred-work/dev-note/future-audio-video-and-assistant-side-output:
|
|
66
|
+
en: 85353325b14af5a8
|
|
67
|
+
zh: c03fb041c2daee7e
|
package/README.md
CHANGED
|
@@ -1,3 +1,138 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Durable image and file attachments for users and maintainers attaching, reusing, or debugging uploads in prompts and commands."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# @qilin-agent/attachment
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
Attach images and generic files to prompts and commands, then reuse them after restarting the same session, without extra setup in the shipped `qilin` composition. Images are validated and normalized before the message is accepted; PNG, JPEG, WebP, and GIF are supported within deployment limits. Other files are stored byte-for-byte without format or size limits, and models read them on demand through saved read-only paths instead of receiving their bytes. Durable session events exclude browser paths, provider URLs, local storage paths, and base64. Stored attachments are never deleted automatically; audio and video have no dedicated handling.
|
|
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 `qilin` 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: '@qilin-agent/attachment-local'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Attach any other file to a prompt
|
|
39
|
+
|
|
40
|
+
Any non-image file attaches to a prompt as a generic file: the exact bytes are saved read-only under the harness home, the message records the file name, byte size, and content digest, and the model receives one line naming the saved path so it can read the content with its file tools only when needed. There is no file-type whitelist and no size limit; what you attach is stored verbatim.
|
|
41
|
+
|
|
42
|
+
### Pass attachments to commands
|
|
43
|
+
|
|
44
|
+
Commands declaring attachment input receive images and generic files in selection order. Commands that do not accept attachments return an error and retain the composer's draft and cards.
|
|
45
|
+
|
|
46
|
+
### Reuse images across the session
|
|
47
|
+
|
|
48
|
+
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.
|
|
49
|
+
|
|
50
|
+
### What can go wrong
|
|
51
|
+
|
|
52
|
+
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.
|
|
53
|
+
|
|
54
|
+
-----
|
|
55
|
+
|
|
56
|
+
<a id="understand-the-implementation"></a>
|
|
57
|
+
## Understand the implementation
|
|
58
|
+
|
|
59
|
+
<details>
|
|
60
|
+
<summary>Implementation internals — click to expand</summary>
|
|
61
|
+
|
|
62
|
+
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).
|
|
63
|
+
|
|
64
|
+
### Design decisions
|
|
65
|
+
|
|
66
|
+
- **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.
|
|
67
|
+
- **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.
|
|
68
|
+
- **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.
|
|
69
|
+
- **Role-neutral image blocks.** The `ImageBlock` content block in `qilin-llm` carries an `ImageAttachmentRef`; provider adapters resolve it into deterministic request versions at an explicit route-chosen target size and byte target, while execution filesystems may map the immutable host object to a model-readable process path.
|
|
70
|
+
- **Error routing by code.** `AttachmentError` re-implements the `HarnessError` shape instead of extending it because the base lives in `qilin-llm`, which depends on this package; consumers use `isAttachmentError` and route on `code`, never on the prototype chain.
|
|
71
|
+
- **Files are verbatim, images are normalized.** `saveFile` commits an existing byte array, `saveFileStream` commits bounded chunks with backpressure and cancellation, `readFileStream` verifies and returns bounded chunks, and `fileHostPath` locates the stored object for read-on-demand projection; neither file write path applies admission limits. The image path keeps its separate normalization, limits, and request-version pipeline. The `FileBlock` content block in `qilin-llm` carries a `FileAttachmentRef`, and request assembly projects it to deterministic handle text for every route.
|
|
72
|
+
|
|
73
|
+
### Service operations
|
|
74
|
+
|
|
75
|
+
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. Host prompt consumers pass ordered text, encoded images, and already resolved file references to `ctx.attachments.admitPromptContent()`; the method persists images and passes file references unchanged. Encoded protocol adapters call `ctx.attachments.admitEncodedFile()`, which checks canonical base64 before delegating to `saveFile`; adapters recognize attachment failures through `ctx.attachments.isAttachmentError()`. Generic-file callers choose `saveFile` for existing bytes or `saveFileStream` for a bounded asynchronous byte source; both return the same durable reference, while `readFileStream` verifies its digest and length during a bounded read. `readImageRequest` derives deterministic route-sized variants whose identity includes the attachment id, transform version, target dimensions, byte target, and encoder settings. The pure `requestImageDimensions` and `longEdgeDimensions` exports compute aspect-preserving dimensions from a total-pixel budget or an exact long edge, so routes 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, streamed writes, 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).
|
|
76
|
+
|
|
77
|
+
### Source map
|
|
78
|
+
|
|
79
|
+
| File | Role |
|
|
80
|
+
|---|---|
|
|
81
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: abstract `AttachmentStore` service and re-exports |
|
|
82
|
+
| [`src/types.ts`](src/types.ts) | Durable vocabulary: references, limits, upload and store payloads |
|
|
83
|
+
| [`src/admission.ts`](src/admission.ts) | Canonical-base64 enforcement and store delegation for encoded image and file uploads |
|
|
84
|
+
| [`src/error.ts`](src/error.ts) | `AttachmentError` class and the `isImageAdmissionError` runtime subset |
|
|
85
|
+
| [`src/brand.ts`](src/brand.ts) | `AttachmentId` branded opaque identifier |
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
-----
|
|
90
|
+
|
|
91
|
+
<a id="further-exploration"></a>
|
|
92
|
+
## Further Exploration
|
|
93
|
+
|
|
94
|
+
For the full service contract and payload types, read the subsystem reference; for the storage that backs this capability, read the local backend.
|
|
95
|
+
|
|
96
|
+
- [Attachment subsystem reference](../../../docs/subsystems/attachment.md) — service contract, payload types, and the `ctx.attachments` Cordis surface.
|
|
97
|
+
- [Local filesystem backend](../attachment-local/README.md) — where your attached images are stored on this machine.
|
|
98
|
+
- [Capability seams](../../../docs/capability-seams.md) — how this capability family is split into roles.
|
|
99
|
+
|
|
100
|
+
-----
|
|
101
|
+
|
|
102
|
+
<a id="model-experience"></a>
|
|
103
|
+
## Model Experience
|
|
104
|
+
|
|
105
|
+
Indirectly, through the provider adapter, which resolves each durable image 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. A generic file never reaches the provider as bytes: every route receives one deterministic handle line naming the file, its byte size, its digest prefix, and the saved read-only path to read with file tools.
|
|
106
|
+
|
|
107
|
+
#### KV Cache effect
|
|
108
|
+
|
|
109
|
+
Adding an image changes the provider request and therefore invalidates the affected request suffix.
|
|
110
|
+
|
|
111
|
+
## Known Limitations and Deferred Work
|
|
112
|
+
|
|
113
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
These limits describe what attachments can and cannot do; they are current package constraints, not a task backlog.
|
|
117
|
+
|
|
118
|
+
- **Raster image limits apply to images only** — PNG, JPEG, WebP, and GIF are accepted as images under deployment limits; every other file is stored verbatim with no type or size limit, and audio and video have no dedicated handling yet.
|
|
119
|
+
- **Attachments are never deleted** — stored images and files are retained indefinitely; nothing removes them automatically.
|
|
120
|
+
- **Unsent drafts are not saved** — a composer draft stays in the browser until you submit the message.
|
|
121
|
+
|
|
122
|
+
<a id="dev-note"></a>
|
|
123
|
+
### Dev Note
|
|
124
|
+
|
|
125
|
+
<details>
|
|
126
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
127
|
+
|
|
128
|
+
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.
|
|
129
|
+
|
|
130
|
+
#### Future: reference-aware garbage collection
|
|
131
|
+
|
|
132
|
+
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.
|
|
133
|
+
|
|
134
|
+
#### Future: audio, video, and assistant-side output
|
|
135
|
+
|
|
136
|
+
Audio and video would need dedicated lifecycle and provider contracts beyond the verbatim file path, 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.
|
|
137
|
+
|
|
138
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "持久图片与文件附件,供用户与维护者在提示词与命令中附加、复用或排查上传内容。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @qilin-agent/attachment
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
把图片与通用文件附加到提示词和命令中,同一会话重启后仍可复用;随附的 `qilin` 组合无需额外配置。图片会在消息被接受前完成校验与规范化;部署限额内支持 PNG、JPEG、WebP 和 GIF。其他文件按字节原样保存,不设格式与大小限制;模型通过保存的只读路径按需读取,而不接收文件字节。持久会话事件不包含浏览器路径、提供方 URL、本地存储路径和 base64。已存储附件不会被自动删除;音频和视频暂无专门处理。
|
|
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
|
+
图片附件端到端可用:把图片附加到提示词或命令,它会自动保存、显示在历史中并发送给模型,无需你再做任何操作。在默认 `qilin` 组合中一切都已接好;自行组合时,一个插件即可启用该能力。
|
|
29
|
+
|
|
30
|
+
### 在提示词中附加图片
|
|
31
|
+
|
|
32
|
+
在客户端 UI 中向用户提示词附加一张或多张图片。每个源图都会在你的消息被处理前接受检查、规范化为提供方无关的 8-bit sRGB/sRGBA 光栅并保存;如果任何一张图片被拒绝,整条消息都会失败且不会发布任何内容。支持的源格式为 PNG、JPEG、WebP 与 GIF;部署方分别控制源图限制、规范化存储限制与路由专用请求限制。下面这一个插件即可启用持久图片附件(随附的 base 组合已经挂载它):
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- name: '@qilin-agent/attachment-local'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 在提示词中附加任意其他文件
|
|
39
|
+
|
|
40
|
+
任何非图片文件都以通用文件的形式附加到提示词:确切字节被只读保存在 harness 主目录下,消息记录文件名、字节数与内容摘要,模型收到一行指出保存路径的文本,只在需要时用文件工具读取内容。没有文件类型白名单,也没有大小上限;你附加什么就原样存什么。
|
|
41
|
+
|
|
42
|
+
### 把附件传给命令
|
|
43
|
+
|
|
44
|
+
声明接受附件的命令会按选择顺序接收图片与通用文件。不接受附件的命令会返回错误,并保留输入区的草稿与附件卡。
|
|
45
|
+
|
|
46
|
+
### 在整个会话中复用图片
|
|
47
|
+
|
|
48
|
+
已保存的规范化图片会保留在对话历史中,并在后续轮次投影为确定性的路由尺寸请求版本;重启后,恢复的会话会显示并复用相同的图片。当前执行文件系统可以映射已存宿主对象时,请求描述符还会携带模型可检查的只读进程路径。回读历史或请求版本时,已存储的字节会与记录的内容比对,因此缺失、损坏或被替换的图片会以错误形式呈现,而不是错误的字节。
|
|
49
|
+
|
|
50
|
+
### 可能出什么问题
|
|
51
|
+
|
|
52
|
+
附加图片时可能被拒绝——格式不受支持、超出大小、像素或尺寸限制,或者字节与声明类型不符——此时整条消息失败。之后,如果磁盘上的图片被删除或损坏,历史读取也可能失败。失败带有稳定错误码,客户端与协议适配器可以用自己的措辞解释它们。
|
|
53
|
+
|
|
54
|
+
-----
|
|
55
|
+
|
|
56
|
+
<a id="understand-the-implementation"></a>
|
|
57
|
+
## 理解实现
|
|
58
|
+
|
|
59
|
+
<details>
|
|
60
|
+
<summary>实现细节——点击展开</summary>
|
|
61
|
+
|
|
62
|
+
本节解释 seam 背后的设计决策,以及实现用户可见行为的服务操作;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
63
|
+
|
|
64
|
+
### 设计决策
|
|
65
|
+
|
|
66
|
+
- **事件前完成规范化与持久化。** 每个源图都会在批次按序发布前完成准备与校验,因此会话日志绝不会引用部分完成或规范化失败的对象。
|
|
67
|
+
- **不可变且保留策略中立。** 对象一经发布即不可变;恢复和 fork 后的会话可能共享它们,因此引用感知的垃圾回收被推迟,而不是与任何单个会话的删除绑定。
|
|
68
|
+
- **读取时校验。** 读取在返回前把字节和元数据与记录的引用比对,请求投影还会完整解码缓存字节,因此缺失、损坏或被替换的对象不会通过校验。
|
|
69
|
+
- **角色无关的图片块。** `qilin-llm` 中的 `ImageBlock` 内容块携带 `ImageAttachmentRef`;提供方适配器按路由显式选定的目标尺寸与字节目标把引用解析为确定性请求版本,执行文件系统则可以把不可变宿主对象映射为模型可读的进程路径。
|
|
70
|
+
- **按错误码路由。** `AttachmentError` 重新实现 `HarnessError` 的结构而不是继承它,因为基类位于 `qilin-llm`,而后者依赖本包;消费方用 `isAttachmentError` 识别错误并按 `code` 路由,绝不依赖原型链。
|
|
71
|
+
- **文件原样,图片规范化。**`saveFile` 提交已有字节数组,`saveFileStream` 以背压和取消语义提交有界分块,`readFileStream` 校验并返回有界分块,`fileHostPath` 定位存储对象供按需读取投影;两种文件写入路径都不设准入限制。图片路径保留其独立的规范化、限额与请求版本流水线。`qilin-llm` 中的 `FileBlock` 内容块承载 `FileAttachmentRef`,请求组装会为每条路由将其投影为确定性的句柄文本。
|
|
72
|
+
|
|
73
|
+
### 服务操作
|
|
74
|
+
|
|
75
|
+
服务族运行同一条准入与存储流程:每个入口都强制执行源批次限制与规范 base64,在发布任何成员前准备提供方无关的规范化附件,再按输入顺序持久提交而不产生部分结果。Host 提示词消费方把有序文本、编码图片和已经解析的文件引用交给 `ctx.attachments.admitPromptContent()`;该方法持久化图片,并让文件引用原样通过。编码协议适配器调用 `ctx.attachments.admitEncodedFile()`,由该方法检查规范 base64 后委托给 `saveFile`;适配器通过 `ctx.attachments.isAttachmentError()` 识别附件错误。通用文件调用方可以用 `saveFile` 提交已有字节,或用 `saveFileStream` 提交有界异步字节源;两者返回相同的持久引用,`readFileStream` 则在有界读取过程中校验摘要与长度。`readImageRequest` 派生确定性的路由尺寸变体,其身份包含附件 id、变换版本、目标尺寸、字节目标及编码参数。纯函数导出 `requestImageDimensions` 与 `longEdgeDimensions` 按总像素预算或精确长边计算保持宽高比的尺寸,使路由与请求定价共享同一套几何计算。`imageHostPath` 只向需要把该位置映射到执行环境的受信任同进程消费方暴露实现拥有的宿主位置。调用方组合有序批次,而实现负责管理压缩并发、缓存与 singleflight。读取、流式写入和投影保留调用方的取消语义。失败带有稳定且机器可读的错误码,运行时即可识别可由调用方修正的准入子集,让每个协议适配器映射自己的词汇;各操作的确切约定见 [`src/index.ts`](src/index.ts) 与 [`src/error.ts`](src/error.ts)。
|
|
76
|
+
|
|
77
|
+
### 源码地图
|
|
78
|
+
|
|
79
|
+
| 文件 | 职责 |
|
|
80
|
+
|---|---|
|
|
81
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:抽象 `AttachmentStore` 服务与再导出 |
|
|
82
|
+
| [`src/types.ts`](src/types.ts) | 持久词汇:引用、限额、上传与存储载荷 |
|
|
83
|
+
| [`src/admission.ts`](src/admission.ts) | 对编码图片和文件上传强制执行规范 base64 并委托存储 |
|
|
84
|
+
| [`src/error.ts`](src/error.ts) | `AttachmentError` 类与 `isImageAdmissionError` 运行时子集 |
|
|
85
|
+
| [`src/brand.ts`](src/brand.ts) | `AttachmentId` 带类型标记的不透明标识符 |
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
-----
|
|
90
|
+
|
|
91
|
+
<a id="further-exploration"></a>
|
|
92
|
+
## 进一步探索
|
|
93
|
+
|
|
94
|
+
完整的服务约定与载荷类型请看子系统参考;支撑这一能力的存储请看本地后端。
|
|
95
|
+
|
|
96
|
+
- [附件子系统参考](../../../docs/subsystems/attachment.zh.md)——服务约定、载荷类型与 `ctx.attachments` 的 Cordis 接口面。
|
|
97
|
+
- [本地文件系统后端](../attachment-local/README.zh.md)——你的附加图片在本机上的存储位置。
|
|
98
|
+
- [能力 seam](../../../docs/capability-seams.zh.md)——本能力家族如何拆分为多个角色。
|
|
99
|
+
|
|
100
|
+
-----
|
|
101
|
+
|
|
102
|
+
<a id="model-experience"></a>
|
|
103
|
+
## 模型体验
|
|
104
|
+
|
|
105
|
+
该包通过提供方适配器间接影响模型;适配器会把每个持久图片引用解析为确切请求版本,并在图片旁发送稳定附件 id 与实际尺寸。执行文件系统可以映射已存对象时,描述符还会包含只读进程路径,以及可写副本使用的匹配扩展名。通用文件的字节永远不会到达提供方:每条路由都收到一行确定性的句柄文本,指出文件名、字节数、摘要前缀,以及供文件工具读取的只读保存路径。
|
|
106
|
+
|
|
107
|
+
#### KV Cache 影响
|
|
108
|
+
|
|
109
|
+
添加图片会改变提供方请求,因此会使受影响的请求后缀失效。
|
|
110
|
+
|
|
111
|
+
## 已知限制与延期工作
|
|
112
|
+
|
|
113
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
这些限制描述了附件能做什么、不能做什么;它们是当前包约束,而非任务积压。
|
|
117
|
+
|
|
118
|
+
- **光栅图片限制只作用于图片**——图片接受部署限额内的 PNG、JPEG、WebP 与 GIF;其他任何文件都原样保存、不设类型与大小限制,音频和视频暂无专门处理。
|
|
119
|
+
- **附件永远不会被删除**——已存储的图片与文件无限期保留;没有任何机制自动移除它们。
|
|
120
|
+
- **未发送的草稿不会保存**——输入区草稿在提交消息前一直留在浏览器中。
|
|
121
|
+
|
|
122
|
+
<a id="dev-note"></a>
|
|
123
|
+
### 开发备注
|
|
124
|
+
|
|
125
|
+
<details>
|
|
126
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
127
|
+
|
|
128
|
+
本开发备注是维护者的工作上下文:尚未决定的探索方向与开放问题。它明确不具权威性——已交付的行为与限制以上文和包代码为准。
|
|
129
|
+
|
|
130
|
+
#### 未来:引用感知的垃圾回收
|
|
131
|
+
|
|
132
|
+
恢复和 fork 后的会话可能共享不可变对象,因此任何保留策略都需要一个能考虑会话血缘的引用模型,之后才能回收对象。目前尚未记录任何决定;本地后端当前保留一切。
|
|
133
|
+
|
|
134
|
+
#### 未来:音频、视频与助手侧输出
|
|
135
|
+
|
|
136
|
+
音频与视频需要原样文件路径之外的专门生命周期与提供方约定;角色无关的 `ImageBlock` 也为助手侧图片输出保留向前兼容空间——当前生产适配器声明只输出文本,因此只有用户内容携带图片。两个方向都尚未决定。
|
|
137
|
+
|
|
138
|
+
</details>
|