@deepblend/dsh-blender-tool 0.7.0 → 0.8.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/lib/committed-preview.js +24 -13
- package/lib/visual-tools.js +11 -8
- package/package.json +2 -2
package/lib/committed-preview.js
CHANGED
|
@@ -8,7 +8,12 @@ const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
|
|
|
8
8
|
|
|
9
9
|
/** Attach the receipt's existing preview, with no compile, render or output-time IO. */
|
|
10
10
|
export async function attachCommittedPreview(ctx, exec, resolved, value, { projectId, summary }) {
|
|
11
|
-
|
|
11
|
+
return attachVerifiedRenderedImage(ctx, exec, resolved, value, { projectId,
|
|
12
|
+
revision: summary?.revision, digest: summary?.digest, artifact: summary?.previews?.[0], kind: 'preview' })
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Read the rendered receipt's pixels once; preserve successful rendering if attachment fails. */
|
|
16
|
+
export async function attachVerifiedRenderedImage(ctx, exec, resolved, value, { projectId, revision, digest, artifact, kind }) {
|
|
12
17
|
if (!artifact) return value
|
|
13
18
|
const cancelled = () => {
|
|
14
19
|
if (exec?.signal?.aborted) throw new BlenderError(BlenderErrorCode.ABORTED,
|
|
@@ -16,18 +21,20 @@ export async function attachCommittedPreview(ctx, exec, resolved, value, { proje
|
|
|
16
21
|
}
|
|
17
22
|
try {
|
|
18
23
|
cancelled()
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
artifact.sourceRevision !== revision || artifact.sourceDigest !== summary.digest ||
|
|
24
|
+
if (!['preview', 'contact-sheet'].includes(kind) || typeof projectId !== 'string' || !projectId || !/^r[0-9]{4,}$/.test(revision) ||
|
|
25
|
+
!/^[a-f0-9]{64}$/.test(digest) || artifact.kind !== kind ||
|
|
26
|
+
artifact.sourceRevision !== revision || artifact.sourceDigest !== digest ||
|
|
23
27
|
artifact.mime !== 'image/png' || typeof artifact.path !== 'string' ||
|
|
24
|
-
!artifact.path.startsWith(`revisions/${revision}
|
|
28
|
+
!artifact.path.startsWith(`revisions/${revision}/${kind === 'contact-sheet' ? 'contact-sheets' : 'previews'}/`) ||
|
|
25
29
|
artifact.path.split('/').some(part => !part || part === '.' || part === '..') ||
|
|
26
30
|
!/^[a-f0-9]{64}$/.test(artifact.sha256) ||
|
|
27
31
|
!Number.isSafeInteger(artifact.bytes) || artifact.bytes <= 0 || artifact.bytes > MAX_IMAGE_BYTES ||
|
|
28
|
-
!Number.isSafeInteger(artifact.width) || artifact.width <= 0 || artifact.width > 2048 ||
|
|
29
|
-
!Number.isSafeInteger(artifact.height) || artifact.height <= 0 || artifact.height > 2048 ||
|
|
30
|
-
typeof artifact.cameraId !== 'string' || !artifact.cameraId || !Number.isSafeInteger(artifact.frame) ||
|
|
32
|
+
!Number.isSafeInteger(artifact.width) || artifact.width <= 0 || artifact.width > (kind === 'contact-sheet' ? 8192 : 2048) ||
|
|
33
|
+
!Number.isSafeInteger(artifact.height) || artifact.height <= 0 || artifact.height > (kind === 'contact-sheet' ? 8192 : 2048) ||
|
|
34
|
+
(kind === 'preview' && (typeof artifact.cameraId !== 'string' || !artifact.cameraId || !Number.isSafeInteger(artifact.frame))) ||
|
|
35
|
+
(kind === 'contact-sheet' && (artifact.slot !== 'preview-current' || !Array.isArray(artifact.views) || artifact.views.length === 0 ||
|
|
36
|
+
artifact.views.length > 64 || new Set(artifact.views).size !== artifact.views.length ||
|
|
37
|
+
artifact.views.some(id => typeof id !== 'string' || !id))) ||
|
|
31
38
|
typeof resolved.studio.readArtifact !== 'function') throw new Error('Invalid saved preview receipt')
|
|
32
39
|
const read = await resolved.studio.readArtifact({ projectId, path: artifact.path })
|
|
33
40
|
cancelled()
|
|
@@ -39,19 +46,23 @@ export async function attachCommittedPreview(ctx, exec, resolved, value, { proje
|
|
|
39
46
|
throw new Error('Saved PNG differs from its receipt')
|
|
40
47
|
}
|
|
41
48
|
const attached = await persistVerifiedPng(resolved.attachments, read.bytes, artifact,
|
|
42
|
-
`preview-${revision}-${artifact.cameraId}-${artifact.frame}.png`, exec?.signal)
|
|
49
|
+
kind === 'contact-sheet' ? `preview-sheet-${revision}.png` : `preview-${revision}-${artifact.cameraId}-${artifact.frame}.png`, exec?.signal)
|
|
43
50
|
cancelled()
|
|
44
51
|
return await imageForCallingModel(ctx, exec, {
|
|
45
52
|
...value, image: attached.image,
|
|
46
|
-
imageSource: { ...artifact, projectId, revision, digest
|
|
47
|
-
text: value.text +
|
|
53
|
+
imageSource: { ...artifact, projectId, revision, digest },
|
|
54
|
+
text: value.text + (kind === 'contact-sheet'
|
|
55
|
+
? '\n\nThe verified contact sheet contains the views just rendered and saved; no additional render or reviewer call was started.'
|
|
56
|
+
: '\n\nThe image uses the preview already saved with this revision; no extra render was started.') +
|
|
48
57
|
(attached.note ? '\n' + attached.note : ''),
|
|
49
58
|
})
|
|
50
59
|
} catch (cause) {
|
|
51
60
|
cancelled()
|
|
52
61
|
if (cause?.code === BlenderErrorCode.ABORTED) throw cause
|
|
53
62
|
return { ...value, image: null,
|
|
54
|
-
text: value.text +
|
|
63
|
+
text: value.text + (kind === 'contact-sheet'
|
|
64
|
+
? '\n\nThe rendered views remain saved, but their contact sheet could not be verified and attached here. '
|
|
65
|
+
: '\n\nThe revision remains saved, but its preview could not be verified and attached here. ') +
|
|
55
66
|
'The model has not seen this image. Use the receipt to inspect the saved artifact; do not recreate the project or repeat the render to repair an attachment.' }
|
|
56
67
|
}
|
|
57
68
|
}
|
package/lib/visual-tools.js
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
42
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
43
|
+
import { attachVerifiedRenderedImage } from './committed-preview.js'
|
|
43
44
|
import { imageForCallingModel } from './image-delivery.js'
|
|
44
45
|
|
|
45
46
|
import { BlenderWarningCode, warning } from '@deepblend/dsh-blender-contracts'
|
|
@@ -87,7 +88,7 @@ function previewViews(ctx) {
|
|
|
87
88
|
description:
|
|
88
89
|
'Render a MULTI-VIEW preview of a revision in one go: the active camera, a three-quarter angle, a ' +
|
|
89
90
|
'top-down view and a subject close-up, all from one Blender process, and measure each one. Returns ' +
|
|
90
|
-
'the images
|
|
91
|
+
'the saved images, a verified contact sheet attached for the calling model, and per-view measurements. Rendering does NOT create a ' +
|
|
91
92
|
'revision — a preview observes a scene rather than changing it — so this is always safe to call and ' +
|
|
92
93
|
'never needs a baseRevision. Use it to look at a scene; use blender_visual_review when you also want a ' +
|
|
93
94
|
'score and a judgment.\n\n' + MEASUREMENT_GLOSSARY,
|
|
@@ -114,12 +115,12 @@ function previewViews(ctx) {
|
|
|
114
115
|
description: 'Override the preview profile\'s sample count; a value above the configured budget is reduced and reported.',
|
|
115
116
|
},
|
|
116
117
|
},
|
|
117
|
-
output:
|
|
118
|
+
output: TOOL_OUTPUT_WITH_IMAGE,
|
|
118
119
|
async execute(args, exec) {
|
|
119
120
|
const resolved = resolveStudio(ctx)
|
|
120
121
|
if (resolved.unavailable !== undefined) return { ok: false, ...resolved.unavailable }
|
|
121
122
|
try {
|
|
122
|
-
const { data, canonicalWarnings } = await canonicalCall(resolved.studio.renderViews({
|
|
123
|
+
const { data, canonicalWarnings } = await canonicalCall(Promise.resolve(resolved.studio.renderViews({
|
|
123
124
|
...definedFields({
|
|
124
125
|
projectId: args.projectId,
|
|
125
126
|
revision: args.revision,
|
|
@@ -130,7 +131,7 @@ function previewViews(ctx) {
|
|
|
130
131
|
samples: args.samples,
|
|
131
132
|
}),
|
|
132
133
|
signal: exec.signal,
|
|
133
|
-
}), warning)
|
|
134
|
+
})).then(({ pngs, ...metadata }) => metadata), warning)
|
|
134
135
|
const scored = resolved.studio.scoreVisualViews({ views: data.views, subjectId: data.subjectId })
|
|
135
136
|
const notes = [
|
|
136
137
|
`Revision: ${data.revision}`,
|
|
@@ -149,13 +150,15 @@ function previewViews(ctx) {
|
|
|
149
150
|
const sheet = (data.revisionPreviews ?? data.contactSheets ?? [])
|
|
150
151
|
notes.push('')
|
|
151
152
|
notes.push('The paths are relative to the project directory. The measured score is what the automated')
|
|
152
|
-
notes.push('scorer makes of these pixels
|
|
153
|
-
notes.push('
|
|
154
|
-
return {
|
|
153
|
+
notes.push('scorer makes of these pixels. Inspect the attached sheet directly; use blender_visual_review')
|
|
154
|
+
notes.push('when you need a separate vision reviewer judgement, not just to obtain these images.')
|
|
155
|
+
return await attachVerifiedRenderedImage(ctx, exec, resolved, {
|
|
155
156
|
ok: true,
|
|
156
157
|
text: renderSuccess(`Rendered ${data.views.length} view(s) of ${data.revision}.`, { ...data, score: scored.score, issues: scored.issues }, { notes, warnings: [...(data.warnings ?? []), ...canonicalWarnings] }),
|
|
157
158
|
data: { ...data, score: scored.score, issues: scored.issues, sheet },
|
|
158
|
-
}
|
|
159
|
+
}, { projectId: args.projectId, revision: data.revision, digest: data.digest,
|
|
160
|
+
artifact: data.previewSheets?.current, kind: 'contact-sheet' })
|
|
161
|
+
|
|
159
162
|
} catch (cause) {
|
|
160
163
|
const failure = renderFailure(cause, 'PREVIEW_VIEWS_FAILED')
|
|
161
164
|
return { ok: false, ...failure }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepblend/dsh-blender-tool",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-preset tool consumer: registers the model-visible DeepBlend Blender tools. Publishes no service.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"./package.json": "./package.json"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@deepblend/dsh-blender-contracts": "0.
|
|
14
|
+
"@deepblend/dsh-blender-contracts": "0.8.0"
|
|
15
15
|
},
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|