@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.
@@ -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
 
@@ -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
@@ -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.20",
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",