@tanstack/ai
Version:
Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.
122 lines (121 loc) • 3.69 kB
JavaScript
import { aiEventClient } from "@tanstack/ai-event-client";
import { streamGenerationResult } from "../stream-generation-result.js";
import { resolveDebugOption } from "../../logger/resolve.js";
import { resolveMediaPrompt } from "../../utilities/media-prompt.js";
import { createGenerationContext, runGenerationStart, runGenerationUsage, runGenerationFinish, runGenerationError } from "../middleware/run.js";
const kind = "image";
function createId(prefix) {
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
}
function generateImage(options) {
if (options.stream) {
return streamGenerationResult(
() => runGenerateImage(options)
);
}
return runGenerateImage(options);
}
async function runGenerateImage(options) {
const {
adapter,
stream: _stream,
debug: _debug,
middleware,
...rest
} = options;
const model = adapter.model;
const requestId = createId("image");
const startTime = Date.now();
const logger = resolveDebugOption(options.debug);
const mwCtx = createGenerationContext({
requestId,
activity: "image",
provider: adapter.name,
model,
modelOptions: rest.modelOptions,
createId
});
await runGenerationStart(middleware, mwCtx);
const resolved = resolveMediaPrompt(rest.prompt);
aiEventClient.emit("image:request:started", {
requestId,
provider: adapter.name,
model,
prompt: resolved.text,
numberOfImages: rest.numberOfImages,
size: rest.size,
...resolved.images.length > 0 && {
imageInputCount: resolved.images.length
},
...resolved.videos.length > 0 && {
videoInputCount: resolved.videos.length
},
...resolved.audios.length > 0 && {
audioInputCount: resolved.audios.length
},
modelOptions: rest.modelOptions,
timestamp: startTime
});
logger.request(`activity=generateImage provider=${adapter.name}`, {
provider: adapter.name,
model
});
try {
const result = await adapter.generateImages({ ...rest, model, logger });
const duration = Date.now() - startTime;
aiEventClient.emit("image:request:completed", {
requestId,
provider: adapter.name,
model,
// GeneratedImage is a discriminated `{ url } | { b64Json }` union, but the
// wire shape on the devtools event is a plain optional pair. Use
// conditional spreads so the emitted record only sets the field actually
// present — `exactOptionalPropertyTypes` rejects `field: undefined`
// against `field?: string` targets.
images: result.images.map((image) => ({
url: image.url,
b64Json: image.b64Json
})),
duration,
modelOptions: rest.modelOptions,
timestamp: Date.now()
});
if (result.usage) {
aiEventClient.emit("image:usage", {
requestId,
model,
usage: result.usage,
modelOptions: rest.modelOptions,
timestamp: Date.now()
});
}
logger.output(`activity=generateImage count=${result.images.length}`, {
count: result.images.length
});
if (result.usage) await runGenerationUsage(middleware, mwCtx, result.usage);
await runGenerationFinish(middleware, mwCtx, {
duration,
usage: result.usage
});
return result;
} catch (error) {
await runGenerationError(middleware, mwCtx, {
error,
duration: Date.now() - startTime
});
logger.errors("generateImage activity failed", {
error,
source: "generateImage"
});
throw error;
}
}
function createImageOptions(options) {
return options;
}
export {
createImageOptions,
generateImage,
kind
};
//# sourceMappingURL=index.js.map