@goodandready/dsh-image-gen 0.11.21 → 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
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
saveAttachmentSafe,
|
|
16
16
|
} from '../providers.js'
|
|
17
17
|
import { buildDualOutputMarkdown } from '../resolve-image.js'
|
|
18
|
-
import { assertBudgetAvailable } from '../cost-meter.js'
|
|
18
|
+
import { assertBudgetAvailable, recordSpend } from '../cost-meter.js'
|
|
19
19
|
import { trackAndAssertLoopGuard } from '../loop-guard.js'
|
|
20
20
|
import { sanitizeErrorAndLogs } from '../security.js'
|
|
21
21
|
|
|
@@ -55,6 +55,7 @@ export function registerProcessingTools(ctx, deps) {
|
|
|
55
55
|
const source = await resolveSource(ctx, exec, args.image)
|
|
56
56
|
if (!source || !source.bytes) throw new Error('Source image not found: ' + args.image)
|
|
57
57
|
const cfg = live()
|
|
58
|
+
assertBudgetAvailable(0.02, cfg.dailyBudgetUsd)
|
|
58
59
|
const res = await removeBackgroundFal(
|
|
59
60
|
{ fetchImpl: fetch, resolveKey: (ref) => resolveApiKey(ctx, ref), cfg },
|
|
60
61
|
{ imageBytes: source.bytes, mediaType: source.mediaType, model: args.model, signal: exec.signal }
|
|
@@ -67,6 +68,7 @@ export function registerProcessingTools(ctx, deps) {
|
|
|
67
68
|
await mkdir(outDir, { recursive: true })
|
|
68
69
|
const filePath = path.join(outDir, name)
|
|
69
70
|
await writeFile(filePath, res.bytes)
|
|
71
|
+
recordSpend(0.02, { ctx, meta: { tool: 'remove_background', model: args.model || 'fal-ai/birefnet' } })
|
|
70
72
|
const summary = buildDualOutputMarkdown({
|
|
71
73
|
action: 'background removed',
|
|
72
74
|
filePath,
|
|
@@ -124,6 +126,7 @@ export function registerProcessingTools(ctx, deps) {
|
|
|
124
126
|
const source = await resolveSource(ctx, exec, args.image)
|
|
125
127
|
if (!source || !source.bytes) throw new Error('Source image not found: ' + args.image)
|
|
126
128
|
const cfg = live()
|
|
129
|
+
assertBudgetAvailable(0.025, cfg.dailyBudgetUsd)
|
|
127
130
|
const res = await upscaleImageFal(
|
|
128
131
|
{ fetchImpl: fetch, resolveKey: (ref) => resolveApiKey(ctx, ref), cfg },
|
|
129
132
|
{ imageBytes: source.bytes, mediaType: source.mediaType, scale: args.scale, prompt: args.prompt, creativity: args.creativity, signal: exec.signal }
|
|
@@ -136,6 +139,7 @@ export function registerProcessingTools(ctx, deps) {
|
|
|
136
139
|
await mkdir(outDir, { recursive: true })
|
|
137
140
|
const filePath = path.join(outDir, name)
|
|
138
141
|
await writeFile(filePath, res.bytes)
|
|
142
|
+
recordSpend(0.025, { ctx, meta: { tool: 'upscale_image', scale: args.scale } })
|
|
139
143
|
const summary = buildDualOutputMarkdown({
|
|
140
144
|
action: `upscaled (${args.scale || 2}x)`,
|
|
141
145
|
filePath,
|
|
@@ -269,6 +273,7 @@ export function registerProcessingTools(ctx, deps) {
|
|
|
269
273
|
await mkdir(outDir, { recursive: true })
|
|
270
274
|
const filePath = path.join(outDir, name)
|
|
271
275
|
await writeFile(filePath, res.bytes)
|
|
276
|
+
recordSpend(0.05, { ctx, meta: { tool: 'blend_images' } })
|
|
272
277
|
const summary = buildDualOutputMarkdown({
|
|
273
278
|
action: 'blended composition',
|
|
274
279
|
filePath,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
saveAttachmentSafe,
|
|
15
15
|
} from '../providers.js'
|
|
16
16
|
import { resolveFallbackChain, executeWithFallback } from '../fallback-router.js'
|
|
17
|
+
import { assertBudgetAvailable, calculateGenerationCost, recordSpend } from '../cost-meter.js'
|
|
17
18
|
import {
|
|
18
19
|
normalizeMatrixStyles,
|
|
19
20
|
buildMatrixCellPrompt,
|
|
@@ -129,6 +130,15 @@ export function registerStyleMatrixTools(ctx, deps) {
|
|
|
129
130
|
let subscriptionImages
|
|
130
131
|
try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch (_err) { subscriptionImages = undefined }
|
|
131
132
|
|
|
133
|
+
const cellCount = activeStyles.length
|
|
134
|
+
const estimatedCost = calculateGenerationCost({
|
|
135
|
+
provider,
|
|
136
|
+
model: cfg.model || cfg.customModel || 'default',
|
|
137
|
+
size: cfg.defaultSize || '1024x1024',
|
|
138
|
+
count: cellCount,
|
|
139
|
+
})
|
|
140
|
+
assertBudgetAvailable(estimatedCost, cfg.dailyBudgetUsd)
|
|
141
|
+
|
|
132
142
|
const chain = resolveFallbackChain(provider, cfg.fallbackProviders, PROVIDER_KEYS)
|
|
133
143
|
const quadrantIds = ['A', 'B', 'C', 'D']
|
|
134
144
|
const matrixId = `matrix-${Date.now().toString(36)}`
|
|
@@ -168,6 +178,12 @@ export function registerStyleMatrixTools(ctx, deps) {
|
|
|
168
178
|
name: filename,
|
|
169
179
|
})
|
|
170
180
|
|
|
181
|
+
const cellCost = Number(gen.cost) || +(estimatedCost / cellCount).toFixed(4)
|
|
182
|
+
recordSpend(cellCost, {
|
|
183
|
+
ctx,
|
|
184
|
+
meta: { provider, model: cfg.model || cfg.customModel, prompt: cellPrompt, seed: cellSeed },
|
|
185
|
+
})
|
|
186
|
+
|
|
171
187
|
const filePath = path.join(outDir, filename)
|
|
172
188
|
await writeFile(filePath, gen.bytes)
|
|
173
189
|
|
package/lib/tools/theme-pair.js
CHANGED
|
@@ -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
|
THEME_STYLES,
|
|
@@ -121,6 +122,14 @@ export function registerThemePairTools(ctx, deps) {
|
|
|
121
122
|
})
|
|
122
123
|
|
|
123
124
|
const primaryProvider = cfg.provider || 'fal'
|
|
125
|
+
const estimatedCost = calculateGenerationCost({
|
|
126
|
+
provider: primaryProvider,
|
|
127
|
+
model: cfg.model || cfg.customModel || 'default',
|
|
128
|
+
size: cfg.defaultSize || '1024x1024',
|
|
129
|
+
count: 2,
|
|
130
|
+
})
|
|
131
|
+
assertBudgetAvailable(estimatedCost, cfg.dailyBudgetUsd)
|
|
132
|
+
|
|
124
133
|
const chain = resolveFallbackChain(primaryProvider, cfg.fallbackChain)
|
|
125
134
|
const baseSeed = typeof args.seed === 'number' ? args.seed : Math.floor(Math.random() * 1000000)
|
|
126
135
|
|
|
@@ -160,6 +169,11 @@ export function registerThemePairTools(ctx, deps) {
|
|
|
160
169
|
width: lightGen.width || aspectPixels[0],
|
|
161
170
|
height: lightGen.height || aspectPixels[1],
|
|
162
171
|
})
|
|
172
|
+
const halfCost = +(estimatedCost / 2).toFixed(4)
|
|
173
|
+
recordSpend(Number(lightGen.cost) || halfCost, {
|
|
174
|
+
ctx,
|
|
175
|
+
meta: { provider: primaryProvider, model: cfg.model || cfg.customModel, prompt: lightPrompt },
|
|
176
|
+
})
|
|
163
177
|
const lightUrl = lightAttachment.attachmentId ? `/dsh-image-gen/image?id=${encodeURIComponent(lightAttachment.attachmentId)}` : ''
|
|
164
178
|
|
|
165
179
|
// 2. Generate Dark theme variant (using matched seed)
|
|
@@ -190,6 +204,10 @@ export function registerThemePairTools(ctx, deps) {
|
|
|
190
204
|
width: darkGen.width || aspectPixels[0],
|
|
191
205
|
height: darkGen.height || aspectPixels[1],
|
|
192
206
|
})
|
|
207
|
+
recordSpend(Number(darkGen.cost) || halfCost, {
|
|
208
|
+
ctx,
|
|
209
|
+
meta: { provider: primaryProvider, model: cfg.model || cfg.customModel, prompt: darkPrompt },
|
|
210
|
+
})
|
|
193
211
|
const darkUrl = darkAttachment.attachmentId ? `/dsh-image-gen/image?id=${encodeURIComponent(darkAttachment.attachmentId)}` : ''
|
|
194
212
|
|
|
195
213
|
// Sidecars
|
package/lib/tools/ui-asset.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
saveAttachmentSafe,
|
|
16
16
|
} from '../providers.js'
|
|
17
17
|
import { resolveFallbackChain, executeWithFallback } from '../fallback-router.js'
|
|
18
|
+
import { assertBudgetAvailable, calculateGenerationCost, recordSpend } from '../cost-meter.js'
|
|
18
19
|
import { buildDualOutputMarkdown } from '../resolve-image.js'
|
|
19
20
|
import {
|
|
20
21
|
UI_ASSET_TYPES,
|
|
@@ -161,6 +162,14 @@ export function registerUiAssetTools(ctx, deps) {
|
|
|
161
162
|
},
|
|
162
163
|
)
|
|
163
164
|
|
|
165
|
+
const estimatedCost = calculateGenerationCost({
|
|
166
|
+
provider,
|
|
167
|
+
model: cfg.model || cfg.customModel || 'default',
|
|
168
|
+
size: cfg.defaultSize || '1024x1024',
|
|
169
|
+
count: 1,
|
|
170
|
+
})
|
|
171
|
+
assertBudgetAvailable(estimatedCost, cfg.dailyBudgetUsd)
|
|
172
|
+
|
|
164
173
|
const chain = resolveFallbackChain(provider, cfg.fallbackProviders, PROVIDER_KEYS)
|
|
165
174
|
const seedVal = args.seed ?? Math.floor(Math.random() * 100000)
|
|
166
175
|
|
|
@@ -201,6 +210,12 @@ export function registerUiAssetTools(ctx, deps) {
|
|
|
201
210
|
name: filename,
|
|
202
211
|
})
|
|
203
212
|
|
|
213
|
+
const effectiveCost = Number(gen.cost) || estimatedCost
|
|
214
|
+
recordSpend(effectiveCost, {
|
|
215
|
+
ctx,
|
|
216
|
+
meta: { provider, model: cfg.model || cfg.customModel, prompt: synthesizedPrompt, seed: seedVal },
|
|
217
|
+
})
|
|
218
|
+
|
|
204
219
|
const sessionCwd = exec.agent?.session?.header?.cwd
|
|
205
220
|
const targetDir = cfg.outputDir || 'generated/images'
|
|
206
221
|
const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-image-gen",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.22",
|
|
4
4
|
"description": "Image generation for DeepSeek Harness: a generate_image tool with pluggable providers — the FAL queue, any OpenAI-compatible images API, or a ChatGPT/Grok subscription with no API key at all. The picture is shown inline in the conversation; the model receives either a link (works with any chat model) or the image itself (needs dsh-vision-bridge or a vision-capable model).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|