glotfile 0.7.1 → 0.7.2
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/dist/server/cli.js
CHANGED
|
@@ -2216,6 +2216,73 @@ var init_batch = __esm({
|
|
|
2216
2216
|
}
|
|
2217
2217
|
});
|
|
2218
2218
|
|
|
2219
|
+
// src/server/ai/pricing.ts
|
|
2220
|
+
function addUsage(into, add) {
|
|
2221
|
+
into.inputTokens += add.inputTokens;
|
|
2222
|
+
into.outputTokens += add.outputTokens;
|
|
2223
|
+
into.cacheCreationInputTokens += add.cacheCreationInputTokens;
|
|
2224
|
+
into.cacheReadInputTokens += add.cacheReadInputTokens;
|
|
2225
|
+
}
|
|
2226
|
+
function usageCostUsd(usage, ai, multiplier = 1) {
|
|
2227
|
+
if (!usage) return void 0;
|
|
2228
|
+
const pricing = resolvePricing(ai);
|
|
2229
|
+
return pricing ? estimateUsageCostUsd(usage, pricing, multiplier) : void 0;
|
|
2230
|
+
}
|
|
2231
|
+
function estimateUsageCostUsd(usage, pricing, multiplier = 1) {
|
|
2232
|
+
const inputCost = (usage.inputTokens + usage.cacheCreationInputTokens * CACHE_WRITE_MULTIPLIER + usage.cacheReadInputTokens * CACHE_READ_MULTIPLIER) * pricing.inputPerMTok;
|
|
2233
|
+
return (inputCost + usage.outputTokens * pricing.outputPerMTok) / 1e6 * multiplier;
|
|
2234
|
+
}
|
|
2235
|
+
function bareModelId(model) {
|
|
2236
|
+
let id = model.trim().toLowerCase();
|
|
2237
|
+
const slash = id.lastIndexOf("/");
|
|
2238
|
+
if (slash !== -1) id = id.slice(slash + 1);
|
|
2239
|
+
const anth = id.lastIndexOf("anthropic.");
|
|
2240
|
+
if (anth !== -1) id = id.slice(anth + "anthropic.".length);
|
|
2241
|
+
return id;
|
|
2242
|
+
}
|
|
2243
|
+
function resolvePricing(ai) {
|
|
2244
|
+
if (ai.inputPricePerMTok !== void 0 && ai.outputPricePerMTok !== void 0) {
|
|
2245
|
+
return { source: "profile", inputPerMTok: ai.inputPricePerMTok, outputPerMTok: ai.outputPricePerMTok };
|
|
2246
|
+
}
|
|
2247
|
+
if (FREE_PROVIDERS.has(ai.provider)) return { source: "builtin", inputPerMTok: 0, outputPerMTok: 0 };
|
|
2248
|
+
const id = bareModelId(ai.model);
|
|
2249
|
+
let best;
|
|
2250
|
+
for (const row of PRICE_TABLE) {
|
|
2251
|
+
if (id.startsWith(row[0]) && (!best || row[0].length > best[0].length)) best = row;
|
|
2252
|
+
}
|
|
2253
|
+
return best ? { source: "builtin", inputPerMTok: best[1], outputPerMTok: best[2] } : null;
|
|
2254
|
+
}
|
|
2255
|
+
var BATCH_PRICE_MULTIPLIER, CACHE_WRITE_MULTIPLIER, CACHE_READ_MULTIPLIER, PRICE_TABLE, FREE_PROVIDERS;
|
|
2256
|
+
var init_pricing = __esm({
|
|
2257
|
+
"src/server/ai/pricing.ts"() {
|
|
2258
|
+
"use strict";
|
|
2259
|
+
BATCH_PRICE_MULTIPLIER = 0.5;
|
|
2260
|
+
CACHE_WRITE_MULTIPLIER = 1.25;
|
|
2261
|
+
CACHE_READ_MULTIPLIER = 0.1;
|
|
2262
|
+
PRICE_TABLE = [
|
|
2263
|
+
["claude-fable-5", 10, 50],
|
|
2264
|
+
["claude-mythos-5", 10, 50],
|
|
2265
|
+
// Deprecated Opus 4.1 / 4.0 cost 3x the 4.5+ generation — they must outrank
|
|
2266
|
+
// the shorter "claude-opus-4" prefix (4-2025 covers the dated Opus 4 full IDs).
|
|
2267
|
+
["claude-opus-4-1", 15, 75],
|
|
2268
|
+
["claude-opus-4-0", 15, 75],
|
|
2269
|
+
["claude-opus-4-2025", 15, 75],
|
|
2270
|
+
["claude-opus-4", 5, 25],
|
|
2271
|
+
["claude-sonnet-4", 3, 15],
|
|
2272
|
+
["claude-haiku-4", 1, 5],
|
|
2273
|
+
["claude-3-5-haiku", 0.8, 4],
|
|
2274
|
+
["gpt-5.5-pro", 30, 180],
|
|
2275
|
+
["gpt-5.5", 5, 30],
|
|
2276
|
+
["gpt-5.4-pro", 30, 180],
|
|
2277
|
+
["gpt-5.4-mini", 0.75, 4.5],
|
|
2278
|
+
["gpt-5.4-nano", 0.2, 1.25],
|
|
2279
|
+
["gpt-5.4", 2.5, 15],
|
|
2280
|
+
["gpt-5.3-codex", 1.75, 14]
|
|
2281
|
+
];
|
|
2282
|
+
FREE_PROVIDERS = /* @__PURE__ */ new Set(["ollama", "claude-code"]);
|
|
2283
|
+
}
|
|
2284
|
+
});
|
|
2285
|
+
|
|
2219
2286
|
// src/server/ai/anthropic.ts
|
|
2220
2287
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2221
2288
|
var AnthropicProvider;
|
|
@@ -2224,6 +2291,7 @@ var init_anthropic = __esm({
|
|
|
2224
2291
|
"use strict";
|
|
2225
2292
|
init_provider();
|
|
2226
2293
|
init_batch();
|
|
2294
|
+
init_pricing();
|
|
2227
2295
|
AnthropicProvider = class {
|
|
2228
2296
|
constructor(config, client) {
|
|
2229
2297
|
this.config = config;
|
|
@@ -2238,9 +2306,25 @@ var init_anthropic = __esm({
|
|
|
2238
2306
|
}
|
|
2239
2307
|
config;
|
|
2240
2308
|
client;
|
|
2309
|
+
usage = { inputTokens: 0, outputTokens: 0, cacheCreationInputTokens: 0, cacheReadInputTokens: 0 };
|
|
2241
2310
|
supportsVision() {
|
|
2242
2311
|
return true;
|
|
2243
2312
|
}
|
|
2313
|
+
recordUsage(usage) {
|
|
2314
|
+
if (!usage) return;
|
|
2315
|
+
addUsage(this.usage, {
|
|
2316
|
+
inputTokens: usage.input_tokens ?? 0,
|
|
2317
|
+
outputTokens: usage.output_tokens ?? 0,
|
|
2318
|
+
cacheCreationInputTokens: usage.cache_creation_input_tokens ?? 0,
|
|
2319
|
+
cacheReadInputTokens: usage.cache_read_input_tokens ?? 0
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
takeUsage() {
|
|
2323
|
+
const taken = this.usage;
|
|
2324
|
+
this.usage = { inputTokens: 0, outputTokens: 0, cacheCreationInputTokens: 0, cacheReadInputTokens: 0 };
|
|
2325
|
+
const any = taken.inputTokens || taken.outputTokens || taken.cacheCreationInputTokens || taken.cacheReadInputTokens;
|
|
2326
|
+
return any ? taken : void 0;
|
|
2327
|
+
}
|
|
2244
2328
|
translate(reqs, onBatchComplete, signal, onMalformedReply) {
|
|
2245
2329
|
return runBatched(reqs, this.config.batchSize, (batch, sig) => this.callBatch(batch, sig), onBatchComplete, signal, onMalformedReply);
|
|
2246
2330
|
}
|
|
@@ -2273,6 +2357,7 @@ var init_anthropic = __esm({
|
|
|
2273
2357
|
output_config: { format: { type: "json_schema", schema: req.schema } },
|
|
2274
2358
|
messages: [{ role: "user", content }]
|
|
2275
2359
|
});
|
|
2360
|
+
this.recordUsage(res.usage);
|
|
2276
2361
|
const text = res.content.find((b) => b.type === "text")?.text ?? "{}";
|
|
2277
2362
|
try {
|
|
2278
2363
|
return JSON.parse(text);
|
|
@@ -2314,6 +2399,7 @@ var init_anthropic = __esm({
|
|
|
2314
2399
|
out.set(entry.custom_id, { type: "failed", error: entry.result.error?.message ?? entry.result.type });
|
|
2315
2400
|
continue;
|
|
2316
2401
|
}
|
|
2402
|
+
this.recordUsage(entry.result.message?.usage);
|
|
2317
2403
|
const text = entry.result.message?.content.find((b) => b.type === "text")?.text ?? "";
|
|
2318
2404
|
try {
|
|
2319
2405
|
out.set(entry.custom_id, { type: "items", items: parseReplyItems(text) });
|
|
@@ -2336,6 +2422,7 @@ var init_anthropic = __esm({
|
|
|
2336
2422
|
output_config: { format: { type: "json_schema", schema: BATCH_SCHEMA } },
|
|
2337
2423
|
messages: [{ role: "user", content }]
|
|
2338
2424
|
}, { signal });
|
|
2425
|
+
this.recordUsage(res.usage);
|
|
2339
2426
|
const text = res.content.find((b) => b.type === "text")?.text ?? "";
|
|
2340
2427
|
return parseReplyItems(text);
|
|
2341
2428
|
}
|
|
@@ -3133,6 +3220,30 @@ var init_pending_batch = __esm({
|
|
|
3133
3220
|
}
|
|
3134
3221
|
});
|
|
3135
3222
|
|
|
3223
|
+
// src/server/log.ts
|
|
3224
|
+
import { appendFileSync, readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
3225
|
+
import { resolve as resolve5 } from "path";
|
|
3226
|
+
function logPath(projectRoot) {
|
|
3227
|
+
return resolve5(projectRoot, ".glotfile", "log.jsonl");
|
|
3228
|
+
}
|
|
3229
|
+
function appendLog(projectRoot, entry) {
|
|
3230
|
+
ensureGlotfileDir(projectRoot);
|
|
3231
|
+
appendFileSync(logPath(projectRoot), JSON.stringify(entry) + "\n", "utf8");
|
|
3232
|
+
}
|
|
3233
|
+
function readLog(projectRoot, limit = 100) {
|
|
3234
|
+
const path = logPath(projectRoot);
|
|
3235
|
+
if (!existsSync7(path)) return [];
|
|
3236
|
+
const lines = readFileSync7(path, "utf8").split("\n").filter((l) => l.trim() !== "");
|
|
3237
|
+
const entries = lines.map((l) => JSON.parse(l));
|
|
3238
|
+
return entries.reverse().slice(0, limit);
|
|
3239
|
+
}
|
|
3240
|
+
var init_log = __esm({
|
|
3241
|
+
"src/server/log.ts"() {
|
|
3242
|
+
"use strict";
|
|
3243
|
+
init_glotfile_dir();
|
|
3244
|
+
}
|
|
3245
|
+
});
|
|
3246
|
+
|
|
3136
3247
|
// src/server/ai/batch-run.ts
|
|
3137
3248
|
function buildBatchJobs(reqs, batchSize) {
|
|
3138
3249
|
const byLocale = /* @__PURE__ */ new Map();
|
|
@@ -3180,7 +3291,9 @@ async function submitBatchTranslation(state, provider, reqs, batchSize, model, p
|
|
|
3180
3291
|
return pending;
|
|
3181
3292
|
}
|
|
3182
3293
|
async function applyBatchResults(load, persist, provider, pending, projectRoot, ai) {
|
|
3294
|
+
provider.takeUsage?.();
|
|
3183
3295
|
const outcomes = await provider.translationBatchResults(pending.batchId);
|
|
3296
|
+
const batchUsage = provider.takeUsage?.();
|
|
3184
3297
|
const fresh = load();
|
|
3185
3298
|
const isStale = (r) => {
|
|
3186
3299
|
const entry = fresh.keys[r.key];
|
|
@@ -3189,13 +3302,19 @@ async function applyBatchResults(load, persist, provider, pending, projectRoot,
|
|
|
3189
3302
|
const applied = [];
|
|
3190
3303
|
const results = [];
|
|
3191
3304
|
const retryReqs = [];
|
|
3192
|
-
|
|
3305
|
+
const stale = [];
|
|
3306
|
+
const jobFailures = [];
|
|
3193
3307
|
for (const job of pending.jobs) {
|
|
3194
3308
|
const outcome = outcomes.get(job.customId);
|
|
3195
3309
|
const itemsById = outcome?.type === "items" ? new Map(outcome.items.map((i) => [i.id, i])) : null;
|
|
3310
|
+
if (!itemsById) {
|
|
3311
|
+
if (!outcome) jobFailures.push({ customId: job.customId, locale: job.locale, type: "missing" });
|
|
3312
|
+
else if (outcome.type === "malformed") jobFailures.push({ customId: job.customId, locale: job.locale, type: "malformed", raw: outcome.raw });
|
|
3313
|
+
else if (outcome.type === "failed") jobFailures.push({ customId: job.customId, locale: job.locale, type: "failed", error: outcome.error });
|
|
3314
|
+
}
|
|
3196
3315
|
for (const stored of job.requests) {
|
|
3197
3316
|
if (isStale(stored)) {
|
|
3198
|
-
|
|
3317
|
+
stale.push({ key: stored.key, locale: stored.targetLocale });
|
|
3199
3318
|
continue;
|
|
3200
3319
|
}
|
|
3201
3320
|
const { sourceHash: _hash, ...req } = stored;
|
|
@@ -3214,7 +3333,20 @@ async function applyBatchResults(load, persist, provider, pending, projectRoot,
|
|
|
3214
3333
|
const retryResults = await runLocaleParallel(
|
|
3215
3334
|
retryReqs,
|
|
3216
3335
|
provider,
|
|
3217
|
-
{
|
|
3336
|
+
{
|
|
3337
|
+
// Record the raw reply so an unparseable retry response is diagnosable
|
|
3338
|
+
// from the activity log instead of vanishing into per-item errors.
|
|
3339
|
+
onMalformedReply: (raw, batchSize, locale) => {
|
|
3340
|
+
appendLog(projectRoot, {
|
|
3341
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3342
|
+
kind: "translate",
|
|
3343
|
+
summary: `Malformed model reply (${locale}, batch of ${batchSize})`,
|
|
3344
|
+
model: pending.model,
|
|
3345
|
+
locale,
|
|
3346
|
+
raw
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
},
|
|
3218
3350
|
ai.concurrency,
|
|
3219
3351
|
void 0,
|
|
3220
3352
|
ai.batchSize
|
|
@@ -3222,10 +3354,34 @@ async function applyBatchResults(load, persist, provider, pending, projectRoot,
|
|
|
3222
3354
|
applied.push(...retryReqs);
|
|
3223
3355
|
results.push(...retryResults);
|
|
3224
3356
|
}
|
|
3357
|
+
const retryUsage = provider.takeUsage?.();
|
|
3358
|
+
const pricing = resolvePricing({ ...ai, model: pending.model });
|
|
3359
|
+
let estimatedCostUsd;
|
|
3360
|
+
if (pricing && (batchUsage || retryUsage)) {
|
|
3361
|
+
estimatedCostUsd = (batchUsage ? estimateUsageCostUsd(batchUsage, pricing, BATCH_PRICE_MULTIPLIER) : 0) + (retryUsage ? estimateUsageCostUsd(retryUsage, pricing) : 0);
|
|
3362
|
+
}
|
|
3363
|
+
let usage;
|
|
3364
|
+
if (batchUsage || retryUsage) {
|
|
3365
|
+
usage = batchUsage ?? { inputTokens: 0, outputTokens: 0, cacheCreationInputTokens: 0, cacheReadInputTokens: 0 };
|
|
3366
|
+
if (retryUsage) addUsage(usage, retryUsage);
|
|
3367
|
+
}
|
|
3225
3368
|
const { written, errors } = applyResults(fresh, applied, results);
|
|
3226
3369
|
persist(fresh);
|
|
3227
3370
|
clearPendingBatch(projectRoot);
|
|
3228
|
-
|
|
3371
|
+
const costSuffix = estimatedCostUsd !== void 0 ? ` (~$${estimatedCostUsd.toFixed(2)})` : "";
|
|
3372
|
+
appendLog(projectRoot, {
|
|
3373
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3374
|
+
kind: "translate",
|
|
3375
|
+
summary: `Applied batch ${pending.batchId}: wrote ${written}, ${errors.length} error(s), ${retryReqs.length} retried, ${stale.length} stale${costSuffix}`,
|
|
3376
|
+
model: pending.model,
|
|
3377
|
+
items: applied.map((r) => ({ id: r.id, key: r.key, source: r.source, targetLocale: r.targetLocale })),
|
|
3378
|
+
results,
|
|
3379
|
+
jobFailures: jobFailures.length ? jobFailures : void 0,
|
|
3380
|
+
stale: stale.length ? stale : void 0,
|
|
3381
|
+
usage,
|
|
3382
|
+
estimatedCostUsd
|
|
3383
|
+
});
|
|
3384
|
+
return { written, errors, staleSkipped: stale.length, retried: retryReqs.length, screenshotsSkipped };
|
|
3229
3385
|
}
|
|
3230
3386
|
var init_batch_run = __esm({
|
|
3231
3387
|
"src/server/ai/batch-run.ts"() {
|
|
@@ -3234,55 +3390,8 @@ var init_batch_run = __esm({
|
|
|
3234
3390
|
init_batch();
|
|
3235
3391
|
init_run();
|
|
3236
3392
|
init_pending_batch();
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
// src/server/ai/pricing.ts
|
|
3241
|
-
function bareModelId(model) {
|
|
3242
|
-
let id = model.trim().toLowerCase();
|
|
3243
|
-
const slash = id.lastIndexOf("/");
|
|
3244
|
-
if (slash !== -1) id = id.slice(slash + 1);
|
|
3245
|
-
const anth = id.lastIndexOf("anthropic.");
|
|
3246
|
-
if (anth !== -1) id = id.slice(anth + "anthropic.".length);
|
|
3247
|
-
return id;
|
|
3248
|
-
}
|
|
3249
|
-
function resolvePricing(ai) {
|
|
3250
|
-
if (ai.inputPricePerMTok !== void 0 && ai.outputPricePerMTok !== void 0) {
|
|
3251
|
-
return { source: "profile", inputPerMTok: ai.inputPricePerMTok, outputPerMTok: ai.outputPricePerMTok };
|
|
3252
|
-
}
|
|
3253
|
-
if (FREE_PROVIDERS.has(ai.provider)) return { source: "builtin", inputPerMTok: 0, outputPerMTok: 0 };
|
|
3254
|
-
const id = bareModelId(ai.model);
|
|
3255
|
-
let best;
|
|
3256
|
-
for (const row of PRICE_TABLE) {
|
|
3257
|
-
if (id.startsWith(row[0]) && (!best || row[0].length > best[0].length)) best = row;
|
|
3258
|
-
}
|
|
3259
|
-
return best ? { source: "builtin", inputPerMTok: best[1], outputPerMTok: best[2] } : null;
|
|
3260
|
-
}
|
|
3261
|
-
var PRICE_TABLE, FREE_PROVIDERS;
|
|
3262
|
-
var init_pricing = __esm({
|
|
3263
|
-
"src/server/ai/pricing.ts"() {
|
|
3264
|
-
"use strict";
|
|
3265
|
-
PRICE_TABLE = [
|
|
3266
|
-
["claude-fable-5", 10, 50],
|
|
3267
|
-
["claude-mythos-5", 10, 50],
|
|
3268
|
-
// Deprecated Opus 4.1 / 4.0 cost 3x the 4.5+ generation — they must outrank
|
|
3269
|
-
// the shorter "claude-opus-4" prefix (4-2025 covers the dated Opus 4 full IDs).
|
|
3270
|
-
["claude-opus-4-1", 15, 75],
|
|
3271
|
-
["claude-opus-4-0", 15, 75],
|
|
3272
|
-
["claude-opus-4-2025", 15, 75],
|
|
3273
|
-
["claude-opus-4", 5, 25],
|
|
3274
|
-
["claude-sonnet-4", 3, 15],
|
|
3275
|
-
["claude-haiku-4", 1, 5],
|
|
3276
|
-
["claude-3-5-haiku", 0.8, 4],
|
|
3277
|
-
["gpt-5.5-pro", 30, 180],
|
|
3278
|
-
["gpt-5.5", 5, 30],
|
|
3279
|
-
["gpt-5.4-pro", 30, 180],
|
|
3280
|
-
["gpt-5.4-mini", 0.75, 4.5],
|
|
3281
|
-
["gpt-5.4-nano", 0.2, 1.25],
|
|
3282
|
-
["gpt-5.4", 2.5, 15],
|
|
3283
|
-
["gpt-5.3-codex", 1.75, 14]
|
|
3284
|
-
];
|
|
3285
|
-
FREE_PROVIDERS = /* @__PURE__ */ new Set(["ollama", "claude-code"]);
|
|
3393
|
+
init_log();
|
|
3394
|
+
init_pricing();
|
|
3286
3395
|
}
|
|
3287
3396
|
});
|
|
3288
3397
|
|
|
@@ -3349,30 +3458,6 @@ var init_estimate = __esm({
|
|
|
3349
3458
|
}
|
|
3350
3459
|
});
|
|
3351
3460
|
|
|
3352
|
-
// src/server/log.ts
|
|
3353
|
-
import { appendFileSync, readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
3354
|
-
import { resolve as resolve5 } from "path";
|
|
3355
|
-
function logPath(projectRoot) {
|
|
3356
|
-
return resolve5(projectRoot, ".glotfile", "log.jsonl");
|
|
3357
|
-
}
|
|
3358
|
-
function appendLog(projectRoot, entry) {
|
|
3359
|
-
ensureGlotfileDir(projectRoot);
|
|
3360
|
-
appendFileSync(logPath(projectRoot), JSON.stringify(entry) + "\n", "utf8");
|
|
3361
|
-
}
|
|
3362
|
-
function readLog(projectRoot, limit = 100) {
|
|
3363
|
-
const path = logPath(projectRoot);
|
|
3364
|
-
if (!existsSync7(path)) return [];
|
|
3365
|
-
const lines = readFileSync7(path, "utf8").split("\n").filter((l) => l.trim() !== "");
|
|
3366
|
-
const entries = lines.map((l) => JSON.parse(l));
|
|
3367
|
-
return entries.reverse().slice(0, limit);
|
|
3368
|
-
}
|
|
3369
|
-
var init_log = __esm({
|
|
3370
|
-
"src/server/log.ts"() {
|
|
3371
|
-
"use strict";
|
|
3372
|
-
init_glotfile_dir();
|
|
3373
|
-
}
|
|
3374
|
-
});
|
|
3375
|
-
|
|
3376
3461
|
// src/server/scan.ts
|
|
3377
3462
|
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
3378
3463
|
import { resolve as resolve6 } from "path";
|
|
@@ -6836,6 +6921,7 @@ function createApi(deps) {
|
|
|
6836
6921
|
persist(fresh);
|
|
6837
6922
|
totalWritten += written;
|
|
6838
6923
|
allErrors.push(...errors);
|
|
6924
|
+
const usage = provider.takeUsage?.();
|
|
6839
6925
|
appendLog(projectRoot, {
|
|
6840
6926
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6841
6927
|
kind: "translate",
|
|
@@ -6846,7 +6932,9 @@ function createApi(deps) {
|
|
|
6846
6932
|
const req = reqById.get(r.id);
|
|
6847
6933
|
return { id: r.id, key: req?.key ?? "", source: req?.source ?? "", targetLocale: req?.targetLocale, context: req?.context, glossary: req?.glossary, screenshot: req ? fresh.keys[req.key]?.screenshot : void 0 };
|
|
6848
6934
|
}),
|
|
6849
|
-
results: batchResults
|
|
6935
|
+
results: batchResults,
|
|
6936
|
+
usage,
|
|
6937
|
+
estimatedCostUsd: usageCostUsd(usage, aiCfg)
|
|
6850
6938
|
});
|
|
6851
6939
|
const ld = (localeDone.get(locale) ?? 0) + batchResults.length;
|
|
6852
6940
|
localeDone.set(locale, ld);
|
|
@@ -6918,11 +7006,14 @@ function createApi(deps) {
|
|
|
6918
7006
|
}, aiCfg.concurrency, void 0, aiCfg.batchSize);
|
|
6919
7007
|
const latest = load();
|
|
6920
7008
|
({ written, errors } = applyResults(latest, toTranslate, results, void 0, force));
|
|
7009
|
+
const usage = provider.takeUsage?.();
|
|
6921
7010
|
const entry = {
|
|
6922
7011
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6923
7012
|
kind: "translate",
|
|
6924
7013
|
summary: `Translated ${toTranslate.length} item(s)`,
|
|
6925
7014
|
model: aiCfg.model,
|
|
7015
|
+
usage,
|
|
7016
|
+
estimatedCostUsd: usageCostUsd(usage, aiCfg),
|
|
6926
7017
|
system: buildSystemPrompt(toTranslate.some((r) => r.plural !== void 0)),
|
|
6927
7018
|
// Log the screenshot PATH only — never the image bytes.
|
|
6928
7019
|
items: toTranslate.map((r) => ({
|
|
@@ -7020,17 +7111,7 @@ function createApi(deps) {
|
|
|
7020
7111
|
if (!supportsBatchTranslate(provider)) {
|
|
7021
7112
|
return c.json({ error: `Provider "${aiCfg.provider}" does not support batch mode.` }, 400);
|
|
7022
7113
|
}
|
|
7023
|
-
const outcome = await applyBatchResults(load, persist, provider, pending, projectRoot,
|
|
7024
|
-
batchSize: aiCfg.batchSize,
|
|
7025
|
-
concurrency: aiCfg.concurrency
|
|
7026
|
-
});
|
|
7027
|
-
appendLog(projectRoot, {
|
|
7028
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7029
|
-
kind: "translate",
|
|
7030
|
-
summary: `Applied batch ${pending.batchId}: wrote ${outcome.written}, ${outcome.retried} retried, ${outcome.staleSkipped} stale`,
|
|
7031
|
-
model: aiCfg.model,
|
|
7032
|
-
results: []
|
|
7033
|
-
});
|
|
7114
|
+
const outcome = await applyBatchResults(load, persist, provider, pending, projectRoot, aiCfg);
|
|
7034
7115
|
console.log(`[batch] applied ${pending.batchId} \u2014 wrote ${outcome.written}, ${outcome.errors.length} error(s)`);
|
|
7035
7116
|
return c.json(outcome);
|
|
7036
7117
|
}));
|
|
@@ -7181,6 +7262,7 @@ function createApi(deps) {
|
|
|
7181
7262
|
const batch = raw;
|
|
7182
7263
|
const fresh = load();
|
|
7183
7264
|
const { written, errors } = applyContext(fresh, chunk2, batch.items ?? [], void 0, body.force === true);
|
|
7265
|
+
const usage = provider.takeUsage?.();
|
|
7184
7266
|
appendLog(projectRoot, {
|
|
7185
7267
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7186
7268
|
kind: "context",
|
|
@@ -7188,7 +7270,9 @@ function createApi(deps) {
|
|
|
7188
7270
|
model: aiCfg.model,
|
|
7189
7271
|
system,
|
|
7190
7272
|
items: chunk2.map((t) => ({ id: t.id, key: t.key, source: t.source })),
|
|
7191
|
-
results: (batch.items ?? []).map((r) => ({ id: r.id, value: r.context, error: r.error }))
|
|
7273
|
+
results: (batch.items ?? []).map((r) => ({ id: r.id, value: r.context, error: r.error })),
|
|
7274
|
+
usage,
|
|
7275
|
+
estimatedCostUsd: usageCostUsd(usage, aiCfg)
|
|
7192
7276
|
});
|
|
7193
7277
|
persist(fresh);
|
|
7194
7278
|
totalWritten += written;
|
|
@@ -7229,6 +7313,7 @@ var init_api = __esm({
|
|
|
7229
7313
|
init_batch_run();
|
|
7230
7314
|
init_pending_batch();
|
|
7231
7315
|
init_estimate();
|
|
7316
|
+
init_pricing();
|
|
7232
7317
|
init_log();
|
|
7233
7318
|
init_schema();
|
|
7234
7319
|
init_run3();
|
|
@@ -7391,6 +7476,7 @@ init_provider();
|
|
|
7391
7476
|
init_batch_run();
|
|
7392
7477
|
init_pending_batch();
|
|
7393
7478
|
init_estimate();
|
|
7479
|
+
init_pricing();
|
|
7394
7480
|
init_log();
|
|
7395
7481
|
init_scan();
|
|
7396
7482
|
init_scanner();
|
|
@@ -7713,11 +7799,14 @@ async function runTranslate(args) {
|
|
|
7713
7799
|
if (!batchCallbackFired) {
|
|
7714
7800
|
({ written, errors } = applyResults(state, toTranslate, results));
|
|
7715
7801
|
}
|
|
7802
|
+
const usage = provider.takeUsage?.();
|
|
7716
7803
|
appendLog(projectRoot, {
|
|
7717
7804
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7718
7805
|
kind: "translate",
|
|
7719
7806
|
summary: `Translated ${toTranslate.length} item(s)`,
|
|
7720
7807
|
model: ai.model,
|
|
7808
|
+
usage,
|
|
7809
|
+
estimatedCostUsd: usageCostUsd(usage, ai),
|
|
7721
7810
|
system: buildSystemPrompt(toTranslate.some((r) => r.plural !== void 0)),
|
|
7722
7811
|
items: toTranslate.map((r) => ({
|
|
7723
7812
|
id: r.id,
|
|
@@ -7752,7 +7841,7 @@ async function applyPending(args, provider, pending, ai) {
|
|
|
7752
7841
|
provider,
|
|
7753
7842
|
pending,
|
|
7754
7843
|
projectRoot,
|
|
7755
|
-
|
|
7844
|
+
ai
|
|
7756
7845
|
);
|
|
7757
7846
|
reportApply(outcome);
|
|
7758
7847
|
}
|