dsh-draw 0.1.0
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/CHANGELOG.md +29 -0
- package/LICENSE +201 -0
- package/README.es.md +194 -0
- package/README.hi.md +194 -0
- package/README.md +194 -0
- package/README.pt.md +194 -0
- package/README.zh.md +194 -0
- package/SECURITY.md +39 -0
- package/THIRD_PARTY_NOTICES.md +21 -0
- package/cordis.patch.yml +48 -0
- package/lib/client.js +4787 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +1429 -0
- package/lib/typert.host.js +26 -0
- package/lib/types/client/DrawResultCard.d.ts +58 -0
- package/lib/types/client/DrawResultCard.d.ts.map +1 -0
- package/lib/types/client/DrawResultCard.js +48 -0
- package/lib/types/client/DrawSettingsTab.d.ts +31 -0
- package/lib/types/client/DrawSettingsTab.d.ts.map +1 -0
- package/lib/types/client/DrawSettingsTab.js +129 -0
- package/lib/types/client/index.d.ts +35 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +98 -0
- package/lib/types/client/locales.d.ts +14 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/locales.js +57 -0
- package/lib/types/client/present.d.ts +80 -0
- package/lib/types/client/present.d.ts.map +1 -0
- package/lib/types/client/present.js +86 -0
- package/lib/types/client/remote.d.ts +268 -0
- package/lib/types/client/remote.d.ts.map +1 -0
- package/lib/types/client/remote.js +15 -0
- package/lib/types/client/styles.d.ts +11 -0
- package/lib/types/client/styles.d.ts.map +1 -0
- package/lib/types/client/styles.js +43 -0
- package/lib/types/config.d.ts +160 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/config.js +230 -0
- package/lib/types/drawer.d.ts +114 -0
- package/lib/types/drawer.d.ts.map +1 -0
- package/lib/types/drawer.js +138 -0
- package/lib/types/engine.d.ts +58 -0
- package/lib/types/engine.d.ts.map +1 -0
- package/lib/types/engine.js +135 -0
- package/lib/types/http.d.ts +89 -0
- package/lib/types/http.d.ts.map +1 -0
- package/lib/types/http.js +127 -0
- package/lib/types/index.d.ts +43 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +78 -0
- package/lib/types/quota.d.ts +69 -0
- package/lib/types/quota.d.ts.map +1 -0
- package/lib/types/quota.js +56 -0
- package/lib/types/router.d.ts +141 -0
- package/lib/types/router.d.ts.map +1 -0
- package/lib/types/router.js +207 -0
- package/lib/types/sanitize.d.ts +40 -0
- package/lib/types/sanitize.d.ts.map +1 -0
- package/lib/types/sanitize.js +103 -0
- package/lib/types/service.d.ts +59 -0
- package/lib/types/service.d.ts.map +1 -0
- package/lib/types/service.js +131 -0
- package/lib/types/session-events.d.ts +66 -0
- package/lib/types/session-events.d.ts.map +1 -0
- package/lib/types/session-events.js +32 -0
- package/lib/types/tool.d.ts +30 -0
- package/lib/types/tool.d.ts.map +1 -0
- package/lib/types/tool.js +131 -0
- package/lib/types/translate.d.ts +64 -0
- package/lib/types/translate.d.ts.map +1 -0
- package/lib/types/translate.js +56 -0
- package/lib/types/typert.host.d.ts +250 -0
- package/lib/types/typert.host.d.ts.map +1 -0
- package/lib/types/typert.host.js +23 -0
- package/lib/types/version.d.ts +10 -0
- package/lib/types/version.d.ts.map +1 -0
- package/lib/types/version.js +9 -0
- package/lib/types/wire.d.ts +699 -0
- package/lib/types/wire.d.ts.map +1 -0
- package/lib/types/wire.js +273 -0
- package/lib/wire-Cc4JZ3jR.js +4370 -0
- package/package.json +179 -0
- package/src/client/DrawResultCard.tsx +100 -0
- package/src/client/DrawSettingsTab.tsx +159 -0
- package/src/client/index.ts +123 -0
- package/src/client/locales.ts +84 -0
- package/src/client/present.ts +137 -0
- package/src/client/remote.ts +44 -0
- package/src/client/styles.ts +44 -0
- package/src/config.ts +358 -0
- package/src/drawer.ts +234 -0
- package/src/engine.ts +182 -0
- package/src/http.ts +161 -0
- package/src/index.ts +93 -0
- package/src/quota.ts +98 -0
- package/src/router.ts +309 -0
- package/src/sanitize.ts +113 -0
- package/src/service.ts +169 -0
- package/src/session-events.ts +70 -0
- package/src/tool.ts +145 -0
- package/src/translate.ts +101 -0
- package/src/typert.host.ts +25 -0
- package/src/version.ts +10 -0
- package/src/wire.ts +417 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `draw` host service: the Typert Remote namespace the settings panel and
|
|
3
|
+
* the result card consume (`draw/status`, `draw/probe`, `draw/setCredential`,
|
|
4
|
+
* `draw/unsetCredential`, `draw/regenerate`). Status snapshots are read-only;
|
|
5
|
+
* credential writes go through the official `ctx.credentials` seam (values
|
|
6
|
+
* never enter a log or a snapshot); regenerate re-runs the full drawer path
|
|
7
|
+
* so a panel regeneration is as durable and quota-accounted as a tool call.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-draw/service
|
|
10
|
+
*/
|
|
11
|
+
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
12
|
+
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
13
|
+
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
14
|
+
import { engineById } from './config.js';
|
|
15
|
+
import { PLUGIN_VERSION } from './version.js';
|
|
16
|
+
import { imageToWire, probeToWire, statusToView, } from './wire.js';
|
|
17
|
+
/**
|
|
18
|
+
* The `draw` Typert Remote service.
|
|
19
|
+
*/
|
|
20
|
+
export class DrawService extends TypertRemoteService {
|
|
21
|
+
/** Per-call bindings (replaced on plugin reload). */
|
|
22
|
+
options;
|
|
23
|
+
/**
|
|
24
|
+
* @param ctx - the mounting context.
|
|
25
|
+
* @param options - runtime bindings.
|
|
26
|
+
*/
|
|
27
|
+
constructor(ctx, options) {
|
|
28
|
+
super(ctx, 'draw');
|
|
29
|
+
this.options = options;
|
|
30
|
+
}
|
|
31
|
+
/** Resolve one engine's credential view for the status snapshot. */
|
|
32
|
+
async credentialView(reference) {
|
|
33
|
+
const credentials = this.options.credentials;
|
|
34
|
+
if (credentials === undefined)
|
|
35
|
+
return { configured: false, writable: false };
|
|
36
|
+
try {
|
|
37
|
+
const info = await credentials.describe(credentialRef(reference));
|
|
38
|
+
return { configured: info.configured, writable: info.writable, ...(info.source !== undefined ? { source: info.source } : {}) };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return { configured: false, writable: false };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Read-only panel snapshot: engine chain, health, credential facts, quota. */
|
|
45
|
+
async status() {
|
|
46
|
+
const { config, router } = this.options;
|
|
47
|
+
const engines = [];
|
|
48
|
+
for (const engine of config.engines) {
|
|
49
|
+
const status = router.statusOf(engine.id);
|
|
50
|
+
const view = statusToView(status ?? { engineId: engine.id, consecutiveFailures: 0, cooldownUntil: null, lastError: null, lastStatus: null }, {
|
|
51
|
+
id: engine.id,
|
|
52
|
+
model: engine.model,
|
|
53
|
+
baseUrl: engine.baseUrl,
|
|
54
|
+
apiKeyRef: engine.apiKeyRef,
|
|
55
|
+
enabled: engine.enabled,
|
|
56
|
+
preferred: engine.id === config.defaultEngine,
|
|
57
|
+
}, await this.credentialView(engine.apiKeyRef));
|
|
58
|
+
engines.push(view);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
pluginVersion: PLUGIN_VERSION,
|
|
62
|
+
engines,
|
|
63
|
+
quota: { maxGenerationsPerSession: config.maxGenerationsPerSession, maxBytesPerSession: config.maxBytesPerSession },
|
|
64
|
+
requestTimeoutMs: config.requestTimeoutMs,
|
|
65
|
+
maxImagesPerCall: config.maxImagesPerCall,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Probe one engine's connectivity without mutating routing health. */
|
|
69
|
+
async probe(engineId) {
|
|
70
|
+
const engine = engineById(this.options.config, engineId);
|
|
71
|
+
if (engine === undefined) {
|
|
72
|
+
return { engineId, reachable: false, httpStatus: null, target: '', note: `unknown engine "${engineId}"`, credentialConfigured: false };
|
|
73
|
+
}
|
|
74
|
+
const outcome = await this.options.router.probe(engine, {
|
|
75
|
+
transport: this.options.drawer.deps.engine.transport,
|
|
76
|
+
resolveCredential: this.options.drawer.deps.engine.resolveCredential,
|
|
77
|
+
});
|
|
78
|
+
return probeToWire(outcome);
|
|
79
|
+
}
|
|
80
|
+
/** Store one API key under the engine's credential reference (credentials seam). */
|
|
81
|
+
async setCredential(engineId, value) {
|
|
82
|
+
const engine = engineById(this.options.config, engineId);
|
|
83
|
+
if (engine === undefined)
|
|
84
|
+
throw new TypeError(`unknown engine "${engineId}"`);
|
|
85
|
+
const credentials = this.options.credentials;
|
|
86
|
+
if (credentials === undefined)
|
|
87
|
+
throw new TypeError('the credential service is not composed on this profile');
|
|
88
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
89
|
+
throw new TypeError('credential value must be a non-empty string');
|
|
90
|
+
await credentials.set(credentialRef(engine.apiKeyRef), value);
|
|
91
|
+
return { engineId, reference: engine.apiKeyRef, note: `stored under the ${engine.apiKeyRef} credential reference` };
|
|
92
|
+
}
|
|
93
|
+
/** Remove a stored API key for the engine's credential reference. */
|
|
94
|
+
async unsetCredential(engineId) {
|
|
95
|
+
const engine = engineById(this.options.config, engineId);
|
|
96
|
+
if (engine === undefined)
|
|
97
|
+
throw new TypeError(`unknown engine "${engineId}"`);
|
|
98
|
+
const credentials = this.options.credentials;
|
|
99
|
+
if (credentials === undefined)
|
|
100
|
+
throw new TypeError('the credential service is not composed on this profile');
|
|
101
|
+
await credentials.unset(credentialRef(engine.apiKeyRef));
|
|
102
|
+
return { engineId, reference: engine.apiKeyRef, note: `removed from the ${engine.apiKeyRef} credential reference` };
|
|
103
|
+
}
|
|
104
|
+
/** Re-run a generation from the result card through the full drawer path. */
|
|
105
|
+
async regenerate(sessionId, args) {
|
|
106
|
+
const sessions = this.options.drawer.deps.sessions?.();
|
|
107
|
+
if (sessions === undefined)
|
|
108
|
+
throw new TypeError('the session store is not composed on this profile');
|
|
109
|
+
const session = sessions.get(SessionId(sessionId));
|
|
110
|
+
if (session === undefined)
|
|
111
|
+
throw new TypeError(`unknown session "${sessionId}"`);
|
|
112
|
+
const outcome = await this.options.drawer.generate(args, { session, source: 'regenerate' });
|
|
113
|
+
if (!outcome.ok)
|
|
114
|
+
throw new TypeError(outcome.message);
|
|
115
|
+
return projectRegenerate(outcome);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Project a successful draw onto the regenerate wire shape. */
|
|
119
|
+
function projectRegenerate(outcome) {
|
|
120
|
+
return {
|
|
121
|
+
engine: outcome.engine,
|
|
122
|
+
model: outcome.model,
|
|
123
|
+
size: outcome.size,
|
|
124
|
+
images: outcome.images.map(imageToWire),
|
|
125
|
+
quota: outcome.quota,
|
|
126
|
+
quotaLimits: outcome.limits,
|
|
127
|
+
fallbackUsed: outcome.fallbackUsed,
|
|
128
|
+
elapsedMs: outcome.elapsedMs,
|
|
129
|
+
attempts: outcome.attempts,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dsh-draw session event: one durable record per completed generation
|
|
3
|
+
* (tool call or panel regenerate), appended to the owning session log. It is
|
|
4
|
+
* log-only (`ignorable`) and exists so quota accounting and the durable audit
|
|
5
|
+
* trail are reconstructable from the log alone. The model-visible input and
|
|
6
|
+
* output of the call ride the loop-owned `tool/call` and `tool/result`
|
|
7
|
+
* events; this event carries the accounting facts those events do not.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-draw/session-events
|
|
10
|
+
*/
|
|
11
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
12
|
+
declare module '@deepseek-ai/dsh-session/types' {
|
|
13
|
+
interface SessionEventMap {
|
|
14
|
+
/**
|
|
15
|
+
* One completed image generation: engine, standardized request, produced
|
|
16
|
+
* bytes, and the durable attachment ids. Log-only UI/accounting state.
|
|
17
|
+
*/
|
|
18
|
+
'draw/generated': DrawGeneratedEvent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Payload of the `draw/generated` session event. */
|
|
22
|
+
export interface DrawGeneratedEvent {
|
|
23
|
+
/** Engine id that produced the images. */
|
|
24
|
+
engine: string;
|
|
25
|
+
/** Engine model name. */
|
|
26
|
+
model: string;
|
|
27
|
+
/** Who requested the generation: the tool body or a panel regenerate. */
|
|
28
|
+
source: 'tool' | 'regenerate';
|
|
29
|
+
/** The standardized prompt, verbatim from the model request. */
|
|
30
|
+
prompt: string;
|
|
31
|
+
/** Standard size vocabulary value of the request. */
|
|
32
|
+
size: 'square' | 'landscape' | 'portrait' | 'auto';
|
|
33
|
+
/** Standard quality value of the request. */
|
|
34
|
+
quality: 'low' | 'medium' | 'high' | 'auto';
|
|
35
|
+
/** Number of images produced. */
|
|
36
|
+
count: number;
|
|
37
|
+
/** Sum of the produced image byte lengths. */
|
|
38
|
+
bytes: number;
|
|
39
|
+
/** Durable attachment ids in result order. */
|
|
40
|
+
attachmentIds: readonly string[];
|
|
41
|
+
/** Engine round-trip latency in milliseconds. */
|
|
42
|
+
elapsedMs: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Append one `draw/generated` event to a session.
|
|
46
|
+
*
|
|
47
|
+
* @param session - owning session.
|
|
48
|
+
* @param event - accounting payload.
|
|
49
|
+
* @returns the logged event.
|
|
50
|
+
*/
|
|
51
|
+
export declare function appendDrawGenerated(session: Session, event: DrawGeneratedEvent): {
|
|
52
|
+
type: "draw/generated";
|
|
53
|
+
seq: number;
|
|
54
|
+
time: number;
|
|
55
|
+
data: DrawGeneratedEvent;
|
|
56
|
+
ignorable?: true;
|
|
57
|
+
} & object;
|
|
58
|
+
/**
|
|
59
|
+
* Collect the `draw/generated` events of a session in log order. Declaration
|
|
60
|
+
* merging narrows `event.type === 'draw/generated'` to the payload type.
|
|
61
|
+
*
|
|
62
|
+
* @param session - session whose log is scanned.
|
|
63
|
+
* @returns the accounting events.
|
|
64
|
+
*/
|
|
65
|
+
export declare function drawGeneratedEvents(session: Session): readonly import('@deepseek-ai/dsh-session').SessionEvent<'draw/generated'>[];
|
|
66
|
+
//# sourceMappingURL=session-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-events.d.ts","sourceRoot":"","sources":["../../src/session-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAEvD,OAAO,QAAQ,gCAAgC,CAAC;IAC9C,UAAU,eAAe;QACvB;;;WAGG;QACH,gBAAgB,EAAE,kBAAkB,CAAA;KACrC;CACF;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAA;IAC7B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAA;IAClD,6CAA6C;IAC7C,OAAO,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAC3C,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB;;;;;;WAI9E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,OAAO,0BAA0B,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAElI"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dsh-draw session event: one durable record per completed generation
|
|
3
|
+
* (tool call or panel regenerate), appended to the owning session log. It is
|
|
4
|
+
* log-only (`ignorable`) and exists so quota accounting and the durable audit
|
|
5
|
+
* trail are reconstructable from the log alone. The model-visible input and
|
|
6
|
+
* output of the call ride the loop-owned `tool/call` and `tool/result`
|
|
7
|
+
* events; this event carries the accounting facts those events do not.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-draw/session-events
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Append one `draw/generated` event to a session.
|
|
13
|
+
*
|
|
14
|
+
* @param session - owning session.
|
|
15
|
+
* @param event - accounting payload.
|
|
16
|
+
* @returns the logged event.
|
|
17
|
+
*/
|
|
18
|
+
export function appendDrawGenerated(session, event) {
|
|
19
|
+
// Two-argument append: the pinned 0.1.0-rc.6 peers have no append-envelope
|
|
20
|
+
// option; the two-argument form typechecks against rc.6 and newer builds.
|
|
21
|
+
return session.append('draw/generated', event);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Collect the `draw/generated` events of a session in log order. Declaration
|
|
25
|
+
* merging narrows `event.type === 'draw/generated'` to the payload type.
|
|
26
|
+
*
|
|
27
|
+
* @param session - session whose log is scanned.
|
|
28
|
+
* @returns the accounting events.
|
|
29
|
+
*/
|
|
30
|
+
export function drawGeneratedEvents(session) {
|
|
31
|
+
return session.events.filter(event => event.type === 'draw/generated');
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `image_generate` tool: the model-facing unified image generation entry.
|
|
3
|
+
* Standard parameters (prompt/size/count/quality/style/engine) flow into the
|
|
4
|
+
* shared drawer path; the canonical value carries the durable attachment
|
|
5
|
+
* references plus quota and routing facts, and the render output embeds the
|
|
6
|
+
* images as attachment content blocks so both text-only surfaces and the web
|
|
7
|
+
* conversation show them.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-draw/tool
|
|
10
|
+
*/
|
|
11
|
+
import type { ResolvedConfig } from './config.ts';
|
|
12
|
+
import type { Drawer } from './drawer.ts';
|
|
13
|
+
/** Tool argument surface (validated by the schema; the drawer re-normalizes). */
|
|
14
|
+
export interface ImageGenerateArgs {
|
|
15
|
+
prompt: string;
|
|
16
|
+
size?: 'square' | 'landscape' | 'portrait' | 'auto';
|
|
17
|
+
count?: number;
|
|
18
|
+
quality?: 'low' | 'medium' | 'high' | 'auto';
|
|
19
|
+
style?: 'natural' | 'vivid';
|
|
20
|
+
engine?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build the `image_generate` tool over one drawer.
|
|
24
|
+
*
|
|
25
|
+
* @param drawer - the shared generation path.
|
|
26
|
+
* @param config - resolved config (prompt cap for the description, timeout budget).
|
|
27
|
+
* @returns the tool definition.
|
|
28
|
+
*/
|
|
29
|
+
export declare function imageGenerateTool(drawer: Drawer, config: ResolvedConfig): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
30
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,aAAa,CAAA;AAEtD,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAA;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAC5C,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,mDA6GvE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `image_generate` tool: the model-facing unified image generation entry.
|
|
3
|
+
* Standard parameters (prompt/size/count/quality/style/engine) flow into the
|
|
4
|
+
* shared drawer path; the canonical value carries the durable attachment
|
|
5
|
+
* references plus quota and routing facts, and the render output embeds the
|
|
6
|
+
* images as attachment content blocks so both text-only surfaces and the web
|
|
7
|
+
* conversation show them.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-draw/tool
|
|
10
|
+
*/
|
|
11
|
+
import { AttachmentId } from '@deepseek-ai/dsh-attachment';
|
|
12
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
13
|
+
/**
|
|
14
|
+
* Build the `image_generate` tool over one drawer.
|
|
15
|
+
*
|
|
16
|
+
* @param drawer - the shared generation path.
|
|
17
|
+
* @param config - resolved config (prompt cap for the description, timeout budget).
|
|
18
|
+
* @returns the tool definition.
|
|
19
|
+
*/
|
|
20
|
+
export function imageGenerateTool(drawer, config) {
|
|
21
|
+
return defineTool({
|
|
22
|
+
name: 'image_generate',
|
|
23
|
+
description: `Generate 1..${config.maxImagesPerCall} static images from a text prompt through the configured image engines (OpenAI Images, Zhipu CogView, or any OpenAI-compatible endpoint). Standard parameters are translated per engine; a failing engine falls back down the configured chain. Results are saved as durable attachments and count against the per-session quota. Prompt cap: ${config.maxPromptLength} characters.`,
|
|
24
|
+
parameters: {
|
|
25
|
+
prompt: { type: 'string', description: 'Image prompt.', required: true },
|
|
26
|
+
size: { type: 'string', enum: ['square', 'landscape', 'portrait', 'auto'], description: 'Composition (default square).' },
|
|
27
|
+
count: { type: 'integer', description: 'Number of images (default 1; capped by configuration).' },
|
|
28
|
+
quality: { type: 'string', enum: ['low', 'medium', 'high', 'auto'], description: 'Quality tier (default auto; dropped for engines without it).' },
|
|
29
|
+
style: { type: 'string', enum: ['natural', 'vivid'], description: 'Style preset (dropped for engines without it).' },
|
|
30
|
+
engine: { type: 'string', description: 'Engine id override (must name a configured engine; omitted = chain order).' },
|
|
31
|
+
},
|
|
32
|
+
output: {
|
|
33
|
+
schema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
ok: { type: 'boolean', const: true },
|
|
37
|
+
engine: { type: 'string' },
|
|
38
|
+
model: { type: 'string' },
|
|
39
|
+
size: { type: 'string' },
|
|
40
|
+
images: {
|
|
41
|
+
type: 'array',
|
|
42
|
+
items: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
attachmentId: { type: 'string' },
|
|
46
|
+
mediaType: { type: 'string' },
|
|
47
|
+
bytes: { type: 'integer' },
|
|
48
|
+
width: { type: 'integer' },
|
|
49
|
+
height: { type: 'integer' },
|
|
50
|
+
name: { type: 'string' },
|
|
51
|
+
},
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
quota: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
generations: { type: 'integer' },
|
|
59
|
+
bytes: { type: 'integer' },
|
|
60
|
+
},
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
},
|
|
63
|
+
limits: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
maxGenerations: { type: 'integer' },
|
|
67
|
+
maxBytes: { type: 'integer' },
|
|
68
|
+
},
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
},
|
|
71
|
+
fallbackUsed: { type: 'boolean' },
|
|
72
|
+
elapsedMs: { type: 'integer' },
|
|
73
|
+
attempts: {
|
|
74
|
+
type: 'array',
|
|
75
|
+
items: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
engine: { type: 'string' },
|
|
79
|
+
phase: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
80
|
+
code: { type: 'string' },
|
|
81
|
+
message: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
82
|
+
status: { oneOf: [{ type: 'integer' }, { type: 'null' }] },
|
|
83
|
+
},
|
|
84
|
+
additionalProperties: false,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
additionalProperties: false,
|
|
89
|
+
},
|
|
90
|
+
render(_args, value) {
|
|
91
|
+
const result = value;
|
|
92
|
+
const lines = [
|
|
93
|
+
`Generated ${result.images.length} image(s) via ${result.engine} (${result.model}, ${result.size})${result.fallbackUsed ? ' after fallback' : ''} in ${result.elapsedMs} ms.`,
|
|
94
|
+
`Quota: ${result.quota.generations}/${result.limits.maxGenerations} calls, ${result.quota.bytes}/${result.limits.maxBytes} bytes this session.`,
|
|
95
|
+
];
|
|
96
|
+
for (const attempt of result.attempts) {
|
|
97
|
+
if (attempt.code === 'ok')
|
|
98
|
+
continue;
|
|
99
|
+
lines.push(`- ${attempt.engine}: ${attempt.code}${attempt.message === undefined ? '' : ` (${attempt.message})`}`);
|
|
100
|
+
}
|
|
101
|
+
const blocks = [{ type: 'text', text: lines.join('\n') }];
|
|
102
|
+
for (const image of result.images) {
|
|
103
|
+
const attachment = {
|
|
104
|
+
attachmentId: AttachmentId(image.attachmentId),
|
|
105
|
+
mediaType: image.mediaType,
|
|
106
|
+
bytes: image.bytes,
|
|
107
|
+
width: image.width,
|
|
108
|
+
height: image.height,
|
|
109
|
+
...(image.name !== undefined ? { name: image.name } : {}),
|
|
110
|
+
};
|
|
111
|
+
blocks.push({ type: 'image', attachment });
|
|
112
|
+
}
|
|
113
|
+
return blocks;
|
|
114
|
+
},
|
|
115
|
+
// The result card reads the canonical value through this replayable
|
|
116
|
+
// presentation projection (the frozen client block exposes only `meta`).
|
|
117
|
+
presentationMeta(_args, value) {
|
|
118
|
+
return value;
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
timeoutMs: config.requestTimeoutMs,
|
|
122
|
+
async execute(args, exec) {
|
|
123
|
+
const outcome = await drawer.generate(args, { session: exec.agent?.session, source: 'tool', signal: exec.signal });
|
|
124
|
+
if (!outcome.ok)
|
|
125
|
+
throw new Error(outcome.message);
|
|
126
|
+
// The schema-inferred canonical value owns mutable arrays; the drawer's
|
|
127
|
+
// frozen records are copied into owned mutable ones here.
|
|
128
|
+
return { ...outcome, images: [...outcome.images], attempts: [...outcome.attempts] };
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard-parameter translation: the tool-level vocabulary
|
|
3
|
+
* (`prompt`/size/count/quality/style) mapped onto one concrete engine's
|
|
4
|
+
* request. Pure functions with no I/O — unit tests pin every mapping.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-draw/translate
|
|
7
|
+
*/
|
|
8
|
+
import type { ResolvedEngineConfig, StandardSize } from './config.ts';
|
|
9
|
+
/** Standard quality vocabulary; `auto` means "leave the engine default". */
|
|
10
|
+
export type StandardQuality = 'low' | 'medium' | 'high' | 'auto';
|
|
11
|
+
/** Standard style vocabulary (engines that lack styles drop it). */
|
|
12
|
+
export type StandardStyle = 'natural' | 'vivid';
|
|
13
|
+
/** Standard tool-level image request. */
|
|
14
|
+
export interface StandardImageRequest {
|
|
15
|
+
/** Image prompt, non-empty and within the configured length cap. */
|
|
16
|
+
prompt: string;
|
|
17
|
+
/** Standard size vocabulary (default `square`). */
|
|
18
|
+
size?: StandardSize;
|
|
19
|
+
/** Number of images (default 1; capped by the engine/config). */
|
|
20
|
+
count?: number;
|
|
21
|
+
/** Standard quality vocabulary (default `auto`). */
|
|
22
|
+
quality?: StandardQuality;
|
|
23
|
+
/** Standard style vocabulary; omitted = engine default. */
|
|
24
|
+
style?: StandardStyle;
|
|
25
|
+
/** Engine id override; omitted = the router's chain order. */
|
|
26
|
+
engine?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Concrete per-engine request body fields. */
|
|
29
|
+
export interface TranslatedImageRequest {
|
|
30
|
+
/** Engine model name. */
|
|
31
|
+
model: string;
|
|
32
|
+
/** Prompt, verbatim. */
|
|
33
|
+
prompt: string;
|
|
34
|
+
/** Concrete engine size string. */
|
|
35
|
+
size: string;
|
|
36
|
+
/** Number of images. */
|
|
37
|
+
n: number;
|
|
38
|
+
/** Included only when the engine declares quality support and it is not `auto`. */
|
|
39
|
+
quality?: StandardQuality;
|
|
40
|
+
/** Included only when the engine declares style support and one was requested. */
|
|
41
|
+
style?: StandardStyle;
|
|
42
|
+
/** How the response delivers bytes. */
|
|
43
|
+
responseFormat: 'b64_json' | 'url';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Normalize unvalidated tool args to a {@link StandardImageRequest}; malformed
|
|
47
|
+
* values fall back to defaults. The prompt passes through verbatim — length
|
|
48
|
+
* validation is the drawer's job (an over-long prompt fails the call loudly
|
|
49
|
+
* instead of being silently truncated).
|
|
50
|
+
*
|
|
51
|
+
* @param args - unvalidated tool arguments.
|
|
52
|
+
* @param maxImagesPerCall - configured cap the requested count clamps to.
|
|
53
|
+
* @returns the normalized request.
|
|
54
|
+
*/
|
|
55
|
+
export declare function normalizeRequest(args: unknown, maxImagesPerCall: number): StandardImageRequest;
|
|
56
|
+
/**
|
|
57
|
+
* Translate one standardized request into a concrete engine request.
|
|
58
|
+
*
|
|
59
|
+
* @param engine - resolved engine configuration.
|
|
60
|
+
* @param request - normalized standard request.
|
|
61
|
+
* @returns the engine request fields.
|
|
62
|
+
*/
|
|
63
|
+
export declare function translateRequest(engine: ResolvedEngineConfig, request: StandardImageRequest): TranslatedImageRequest;
|
|
64
|
+
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/translate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAErE,4EAA4E;AAC5E,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;AAEhE,oEAAoE;AACpE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,CAAA;AAE/C,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACrC,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,wBAAwB;IACxB,CAAC,EAAE,MAAM,CAAA;IACT,mFAAmF;IACnF,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,kFAAkF;IAClF,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,uCAAuC;IACvC,cAAc,EAAE,UAAU,GAAG,KAAK,CAAA;CACnC;AAMD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,GAAG,oBAAoB,CAe9F;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,oBAAoB,GAAG,sBAAsB,CAYpH"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard-parameter translation: the tool-level vocabulary
|
|
3
|
+
* (`prompt`/size/count/quality/style) mapped onto one concrete engine's
|
|
4
|
+
* request. Pure functions with no I/O — unit tests pin every mapping.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-draw/translate
|
|
7
|
+
*/
|
|
8
|
+
const SIZES = ['square', 'landscape', 'portrait', 'auto'];
|
|
9
|
+
const QUALITIES = ['low', 'medium', 'high', 'auto'];
|
|
10
|
+
const STYLES = ['natural', 'vivid'];
|
|
11
|
+
/**
|
|
12
|
+
* Normalize unvalidated tool args to a {@link StandardImageRequest}; malformed
|
|
13
|
+
* values fall back to defaults. The prompt passes through verbatim — length
|
|
14
|
+
* validation is the drawer's job (an over-long prompt fails the call loudly
|
|
15
|
+
* instead of being silently truncated).
|
|
16
|
+
*
|
|
17
|
+
* @param args - unvalidated tool arguments.
|
|
18
|
+
* @param maxImagesPerCall - configured cap the requested count clamps to.
|
|
19
|
+
* @returns the normalized request.
|
|
20
|
+
*/
|
|
21
|
+
export function normalizeRequest(args, maxImagesPerCall) {
|
|
22
|
+
const record = typeof args === 'object' && args !== null ? args : {};
|
|
23
|
+
const prompt = typeof record['prompt'] === 'string' ? record['prompt'] : '';
|
|
24
|
+
const rawSize = record['size'];
|
|
25
|
+
const size = typeof rawSize === 'string' && SIZES.includes(rawSize) ? rawSize : 'square';
|
|
26
|
+
const rawCount = record['count'];
|
|
27
|
+
const parsedCount = typeof rawCount === 'number' ? Math.trunc(rawCount) : 1;
|
|
28
|
+
const count = Math.min(Math.max(Number.isFinite(parsedCount) ? parsedCount : 1, 1), maxImagesPerCall);
|
|
29
|
+
const rawQuality = record['quality'];
|
|
30
|
+
const quality = typeof rawQuality === 'string' && QUALITIES.includes(rawQuality) ? rawQuality : 'auto';
|
|
31
|
+
const rawStyle = record['style'];
|
|
32
|
+
const style = typeof rawStyle === 'string' && STYLES.includes(rawStyle) ? rawStyle : undefined;
|
|
33
|
+
const rawEngine = record['engine'];
|
|
34
|
+
const engine = typeof rawEngine === 'string' && rawEngine.length > 0 ? rawEngine : undefined;
|
|
35
|
+
return { prompt, size, count, quality, ...(style !== undefined ? { style } : {}), ...(engine !== undefined ? { engine } : {}) };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Translate one standardized request into a concrete engine request.
|
|
39
|
+
*
|
|
40
|
+
* @param engine - resolved engine configuration.
|
|
41
|
+
* @param request - normalized standard request.
|
|
42
|
+
* @returns the engine request fields.
|
|
43
|
+
*/
|
|
44
|
+
export function translateRequest(engine, request) {
|
|
45
|
+
const size = engine.sizeMap[request.size ?? 'square'];
|
|
46
|
+
const requestBody = {
|
|
47
|
+
model: engine.model,
|
|
48
|
+
prompt: request.prompt,
|
|
49
|
+
size,
|
|
50
|
+
n: request.count ?? 1,
|
|
51
|
+
...(engine.qualitySupported && request.quality !== 'auto' ? { quality: request.quality } : {}),
|
|
52
|
+
...(engine.styleSupported && request.style !== undefined ? { style: request.style } : {}),
|
|
53
|
+
responseFormat: engine.responseFormat,
|
|
54
|
+
};
|
|
55
|
+
return requestBody;
|
|
56
|
+
}
|