@goodandready/dsh-image-gen 0.11.20 → 0.11.22
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/attachment-helper.js +14 -0
- package/lib/commands.js +16 -8
- package/lib/config-schema.js +28 -3
- package/lib/generation-helpers.js +266 -250
- package/lib/index.js +12 -2
- package/lib/loop-guard.js +32 -8
- package/lib/provider-utils.js +40 -1
- package/lib/providers/backends/local.js +1 -1
- package/lib/register-tools.js +42 -17
- package/lib/tools/character-sheet.js +14 -0
- package/lib/tools/diagram.js +14 -0
- package/lib/tools/editing.js +9 -0
- package/lib/tools/generation.js +505 -496
- package/lib/tools/processing-basic.js +6 -1
- package/lib/tools/style-matrix.js +16 -0
- package/lib/tools/theme-pair.js +18 -0
- package/lib/tools/ui-asset.js +15 -0
- package/package.json +1 -1
package/lib/provider-utils.js
CHANGED
|
@@ -105,14 +105,53 @@ export function formatA1111Parameters(metadata = {}) {
|
|
|
105
105
|
import { createHash } from 'node:crypto'
|
|
106
106
|
|
|
107
107
|
/** Deterministic sha256 hash for image generation caching. */
|
|
108
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Deterministic sha256 hash for image generation caching (#170, #357).
|
|
110
|
+
* Incorporates full provider job identity: provider, model, prompt, seed, size, style,
|
|
111
|
+
* format, aspect ratio, negative prompt, quality, strength, and source/mask image content hashes.
|
|
112
|
+
*/
|
|
113
|
+
export function computeGenerationHash({
|
|
114
|
+
provider,
|
|
115
|
+
model,
|
|
116
|
+
prompt,
|
|
117
|
+
seed,
|
|
118
|
+
size,
|
|
119
|
+
style,
|
|
120
|
+
format,
|
|
121
|
+
aspectRatio,
|
|
122
|
+
negativePrompt,
|
|
123
|
+
quality,
|
|
124
|
+
strength,
|
|
125
|
+
sourceImage,
|
|
126
|
+
maskImage,
|
|
127
|
+
reference,
|
|
128
|
+
} = {}) {
|
|
129
|
+
const hashContent = (item) => {
|
|
130
|
+
if (!item) return ''
|
|
131
|
+
if (Buffer.isBuffer(item)) return createHash('sha256').update(item).digest('hex').slice(0, 16)
|
|
132
|
+
if (typeof item === 'object' && item.bytes) {
|
|
133
|
+
const b = Buffer.isBuffer(item.bytes) ? item.bytes : Buffer.from(item.bytes)
|
|
134
|
+
return createHash('sha256').update(b).digest('hex').slice(0, 16)
|
|
135
|
+
}
|
|
136
|
+
return String(item).trim()
|
|
137
|
+
}
|
|
138
|
+
|
|
109
139
|
const norm = [
|
|
140
|
+
'v2', // namespace versioning (#357)
|
|
110
141
|
String(provider || '').trim().toLowerCase(),
|
|
111
142
|
String(model || '').trim().toLowerCase(),
|
|
112
143
|
String(prompt || '').trim(),
|
|
113
144
|
String(seed ?? ''),
|
|
114
145
|
String(size || '').trim().toLowerCase(),
|
|
115
146
|
String(style || '').trim().toLowerCase(),
|
|
147
|
+
String(format || '').trim().toLowerCase(),
|
|
148
|
+
String(aspectRatio || '').trim().toLowerCase(),
|
|
149
|
+
String(negativePrompt || '').trim(),
|
|
150
|
+
String(quality || '').trim().toLowerCase(),
|
|
151
|
+
strength !== undefined && strength !== null ? String(strength) : '',
|
|
152
|
+
hashContent(sourceImage),
|
|
153
|
+
hashContent(maskImage),
|
|
154
|
+
hashContent(reference),
|
|
116
155
|
].join('|')
|
|
117
156
|
return createHash('sha256').update(norm).digest('hex')
|
|
118
157
|
}
|
|
@@ -24,7 +24,7 @@ export function createLocalGenerator(deps, job) {
|
|
|
24
24
|
const base = String(cfg.localBaseURL || '').replace(/\/+$/, '')
|
|
25
25
|
if (!base) throw new Error('Local image provider: server address is not configured (Settings → Image generation)')
|
|
26
26
|
const [width, height] = pxSize(aspectPixels, size)
|
|
27
|
-
const kind = cfg.localKind === 'a1111' ? 'a1111' : 'comfyui'
|
|
27
|
+
const kind = (cfg.localKind === 'a1111' || cfg.localKind === 'automatic1111') ? 'a1111' : 'comfyui'
|
|
28
28
|
|
|
29
29
|
if (kind === 'a1111') {
|
|
30
30
|
const isImg2Img = Boolean(source && source.bytes)
|
package/lib/register-tools.js
CHANGED
|
@@ -26,6 +26,31 @@ import { registerDiagramTools } from './tools/diagram.js'
|
|
|
26
26
|
* @param {object} deps shared runtime from apply()
|
|
27
27
|
*/
|
|
28
28
|
export function registerAllTools(ctx, deps) {
|
|
29
|
+
const originalToolsRegister = ctx?.tools?.register ? ctx.tools.register.bind(ctx.tools) : null
|
|
30
|
+
const wrappedTools = originalToolsRegister ? Object.create(ctx.tools, {
|
|
31
|
+
register: {
|
|
32
|
+
value: (def) => {
|
|
33
|
+
const originalExecute = def.execute
|
|
34
|
+
const wrappedDef = {
|
|
35
|
+
...def,
|
|
36
|
+
execute: async (args, exec) => {
|
|
37
|
+
const currentCfg = typeof deps.live === 'function' ? deps.live() : (deps.config || {})
|
|
38
|
+
if (currentCfg && currentCfg.enabled === false) {
|
|
39
|
+
throw new Error('Image generation is disabled in settings (dsh-image-gen.enabled is false); enable it in Settings → Image generation.')
|
|
40
|
+
}
|
|
41
|
+
return originalExecute(args, exec)
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
return originalToolsRegister(wrappedDef)
|
|
45
|
+
},
|
|
46
|
+
writable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
},
|
|
49
|
+
}) : ctx?.tools
|
|
50
|
+
|
|
51
|
+
const targetCtx = originalToolsRegister ? Object.create(ctx, {
|
|
52
|
+
tools: { value: wrappedTools, writable: true, configurable: true }
|
|
53
|
+
}) : ctx
|
|
29
54
|
const cfg = typeof deps.live === 'function' ? deps.live() : (deps.config || {})
|
|
30
55
|
const profile = cfg.toolsetProfile || 'minimal'
|
|
31
56
|
const isAll = profile === 'all'
|
|
@@ -36,37 +61,37 @@ export function registerAllTools(ctx, deps) {
|
|
|
36
61
|
const creative = isAll || Boolean(sets.creative || cfg.toolsetCreative)
|
|
37
62
|
|
|
38
63
|
// 1. Core tools: generate_image, edit_image, inspect_image_quality (<= 3 tools in minimal)
|
|
39
|
-
registerGenerationTools(
|
|
40
|
-
registerEditingTools(
|
|
41
|
-
registerInspectTools(
|
|
64
|
+
registerGenerationTools(targetCtx, deps, { coreOnly: !creative })
|
|
65
|
+
registerEditingTools(targetCtx, deps, { coreOnly: !processing })
|
|
66
|
+
registerInspectTools(targetCtx, deps, { coreOnly: !processing })
|
|
42
67
|
|
|
43
68
|
// 2. Processing toolset
|
|
44
69
|
if (processing) {
|
|
45
|
-
registerProcessingTools(
|
|
46
|
-
registerProcessingAdvancedTools(
|
|
47
|
-
registerVisionOcrTools(
|
|
70
|
+
registerProcessingTools(targetCtx, deps)
|
|
71
|
+
registerProcessingAdvancedTools(targetCtx, deps)
|
|
72
|
+
registerVisionOcrTools(targetCtx, deps)
|
|
48
73
|
}
|
|
49
74
|
|
|
50
75
|
// 3. Design toolset
|
|
51
76
|
if (design) {
|
|
52
|
-
registerAnchorTools(
|
|
53
|
-
registerUiAssetTools(
|
|
54
|
-
registerStyleMatrixTools(
|
|
55
|
-
registerThemePairTools(
|
|
56
|
-
registerCharacterSheetTools(
|
|
57
|
-
registerDiagramTools(
|
|
77
|
+
registerAnchorTools(targetCtx, deps)
|
|
78
|
+
registerUiAssetTools(targetCtx, deps)
|
|
79
|
+
registerStyleMatrixTools(targetCtx, deps)
|
|
80
|
+
registerThemePairTools(targetCtx, deps)
|
|
81
|
+
registerCharacterSheetTools(targetCtx, deps)
|
|
82
|
+
registerDiagramTools(targetCtx, deps)
|
|
58
83
|
}
|
|
59
84
|
|
|
60
85
|
// 4. Frontend toolset
|
|
61
86
|
if (frontend) {
|
|
62
|
-
registerFrontendTools(
|
|
63
|
-
registerResponsiveTools(
|
|
87
|
+
registerFrontendTools(targetCtx, deps)
|
|
88
|
+
registerResponsiveTools(targetCtx, deps)
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
// 5. Creative toolset
|
|
67
92
|
if (creative) {
|
|
68
|
-
registerSketchTools(
|
|
69
|
-
registerSpritesheetTools(
|
|
70
|
-
registerPatternTools(
|
|
93
|
+
registerSketchTools(targetCtx, deps)
|
|
94
|
+
registerSpritesheetTools(targetCtx, deps)
|
|
95
|
+
registerPatternTools(targetCtx, deps)
|
|
71
96
|
}
|
|
72
97
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
saveAttachmentSafe,
|
|
14
14
|
} from '../providers.js'
|
|
15
15
|
import { resolveFallbackChain, executeWithFallback } from '../fallback-router.js'
|
|
16
|
+
import { assertBudgetAvailable, calculateGenerationCost, recordSpend } from '../cost-meter.js'
|
|
16
17
|
import { renderToolOutput } from '../attachment-helper.js'
|
|
17
18
|
import {
|
|
18
19
|
CHARACTER_SHEET_LAYOUTS,
|
|
@@ -114,6 +115,14 @@ export function registerCharacterSheetTools(ctx, deps) {
|
|
|
114
115
|
|
|
115
116
|
const aspectPixels = ASPECT_RATIOS[sheetPlan.aspectRatio] || ASPECT_RATIOS['16:9']
|
|
116
117
|
|
|
118
|
+
const estimatedCost = calculateGenerationCost({
|
|
119
|
+
provider: activeProvider,
|
|
120
|
+
model: cfg.model || cfg.customModel || 'default',
|
|
121
|
+
size: cfg.defaultSize || '1024x1024',
|
|
122
|
+
count: 1,
|
|
123
|
+
})
|
|
124
|
+
assertBudgetAvailable(estimatedCost, cfg.dailyBudgetUsd)
|
|
125
|
+
|
|
117
126
|
const jobPayload = {
|
|
118
127
|
prompt: sheetPlan.prompt,
|
|
119
128
|
negativePrompt: sheetPlan.negativePrompt,
|
|
@@ -157,6 +166,11 @@ export function registerCharacterSheetTools(ctx, deps) {
|
|
|
157
166
|
}
|
|
158
167
|
}
|
|
159
168
|
|
|
169
|
+
recordSpend(Number(genResult.cost) || estimatedCost, {
|
|
170
|
+
ctx,
|
|
171
|
+
meta: { provider: usedProvider, model: cfg.model || cfg.customModel, prompt: sheetPlan.prompt, seed },
|
|
172
|
+
})
|
|
173
|
+
|
|
160
174
|
const response = {
|
|
161
175
|
prompt: sheetPlan.prompt,
|
|
162
176
|
characterDescription: args.prompt,
|
package/lib/tools/diagram.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
saveAttachmentSafe,
|
|
12
12
|
} from '../providers.js'
|
|
13
13
|
import { resolveFallbackChain, executeWithFallback } from '../fallback-router.js'
|
|
14
|
+
import { assertBudgetAvailable, calculateGenerationCost, recordSpend } from '../cost-meter.js'
|
|
14
15
|
import { renderToolOutput } from '../attachment-helper.js'
|
|
15
16
|
import {
|
|
16
17
|
DIAGRAM_3D_STYLES,
|
|
@@ -108,6 +109,14 @@ export function registerDiagramTools(ctx, deps) {
|
|
|
108
109
|
const fallbackChain = resolveFallbackChain(activeProvider, cfg)
|
|
109
110
|
const aspectPixels = ASPECT_RATIOS[plan.aspectRatio] || ASPECT_RATIOS['16:9']
|
|
110
111
|
|
|
112
|
+
const estimatedCost = calculateGenerationCost({
|
|
113
|
+
provider: activeProvider,
|
|
114
|
+
model: cfg.model || cfg.customModel || 'default',
|
|
115
|
+
size: cfg.defaultSize || '1024x1024',
|
|
116
|
+
count: 1,
|
|
117
|
+
})
|
|
118
|
+
assertBudgetAvailable(estimatedCost, cfg.dailyBudgetUsd)
|
|
119
|
+
|
|
111
120
|
const jobPayload = {
|
|
112
121
|
prompt: plan.prompt,
|
|
113
122
|
negativePrompt: plan.negativePrompt,
|
|
@@ -150,6 +159,11 @@ export function registerDiagramTools(ctx, deps) {
|
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
|
|
162
|
+
recordSpend(Number(genResult.cost) || estimatedCost, {
|
|
163
|
+
ctx,
|
|
164
|
+
meta: { provider: usedProvider, model: cfg.model || cfg.customModel, prompt: plan.prompt, seed },
|
|
165
|
+
})
|
|
166
|
+
|
|
153
167
|
return {
|
|
154
168
|
title: args.title || 'System Architecture',
|
|
155
169
|
style: plan.style,
|
package/lib/tools/editing.js
CHANGED
|
@@ -84,6 +84,9 @@ export function registerEditingTools(ctx, deps, options = {}) {
|
|
|
84
84
|
async execute(args, exec) {
|
|
85
85
|
try {
|
|
86
86
|
const cfg = live()
|
|
87
|
+
if (cfg && cfg.enabled === false) {
|
|
88
|
+
throw new Error('Image generation is disabled in settings (dsh-image-gen.enabled is false); enable it in Settings → Image generation.')
|
|
89
|
+
}
|
|
87
90
|
trackAndAssertLoopGuard(exec?.agent?.session?.id || 'default', { limit: cfg.loopGuardLimit })
|
|
88
91
|
const estimatedCostEdit = calculateGenerationCost({
|
|
89
92
|
provider: cfg.provider || 'fal',
|
|
@@ -219,6 +222,9 @@ export function registerEditingTools(ctx, deps, options = {}) {
|
|
|
219
222
|
async execute(args, exec) {
|
|
220
223
|
try {
|
|
221
224
|
const cfg = live()
|
|
225
|
+
if (cfg && cfg.enabled === false) {
|
|
226
|
+
throw new Error('Image generation is disabled in settings (dsh-image-gen.enabled is false); enable it in Settings → Image generation.')
|
|
227
|
+
}
|
|
222
228
|
trackAndAssertLoopGuard(exec?.agent?.session?.id || 'default', { limit: cfg.loopGuardLimit })
|
|
223
229
|
const count = normalizeCount(args.count || 1)
|
|
224
230
|
const estimatedCostVary = calculateGenerationCost({
|
|
@@ -357,6 +363,9 @@ export function registerEditingTools(ctx, deps, options = {}) {
|
|
|
357
363
|
async execute(args, exec) {
|
|
358
364
|
try {
|
|
359
365
|
const cfg = live()
|
|
366
|
+
if (cfg && cfg.enabled === false) {
|
|
367
|
+
throw new Error('Image generation is disabled in settings (dsh-image-gen.enabled is false); enable it in Settings → Image generation.')
|
|
368
|
+
}
|
|
360
369
|
trackAndAssertLoopGuard(exec?.agent?.session?.id || 'default', { limit: cfg.loopGuardLimit })
|
|
361
370
|
const count = normalizeCount(args.count || 1)
|
|
362
371
|
const estimatedCost = calculateGenerationCost({
|