workers-ai-provider
Version:
Workers AI Provider for the vercel AI SDK
115 lines (97 loc) • 2.8 kB
text/typescript
import type { ImageModelV1, ImageModelV1CallWarning } from "@ai-sdk/provider";
import type { WorkersAIImageConfig } from "./workersai-image-config";
import type { WorkersAIImageSettings } from "./workersai-image-settings";
import type { ImageGenerationModels } from "./workersai-models";
export class WorkersAIImageModel implements ImageModelV1 {
readonly specificationVersion = "v1";
get maxImagesPerCall(): number {
return this.settings.maxImagesPerCall ?? 1;
}
get provider(): string {
return this.config.provider;
}
constructor(
readonly modelId: ImageGenerationModels,
readonly settings: WorkersAIImageSettings,
readonly config: WorkersAIImageConfig,
) {}
async doGenerate({
prompt,
n,
size,
aspectRatio,
seed,
// headers,
// abortSignal,
}: Parameters<ImageModelV1["doGenerate"]>[0]): Promise<
Awaited<ReturnType<ImageModelV1["doGenerate"]>>
> {
const { width, height } = getDimensionsFromSizeString(size);
const warnings: Array<ImageModelV1CallWarning> = [];
if (aspectRatio != null) {
warnings.push({
details: "This model does not support aspect ratio. Use `size` instead.",
setting: "aspectRatio",
type: "unsupported-setting",
});
}
const generateImage = async () => {
const outputStream: ReadableStream<Uint8Array> = await this.config.binding.run(
this.modelId,
{
height,
prompt,
seed,
width,
},
);
// Convert the output stream to a Uint8Array.
return streamToUint8Array(outputStream);
};
const images: Uint8Array[] = await Promise.all(
Array.from({ length: n }, () => generateImage()),
);
// type AiTextToImageOutput = ReadableStream<Uint8Array>;
return {
images,
response: {
headers: {},
modelId: this.modelId,
timestamp: new Date(),
},
warnings,
};
}
}
function getDimensionsFromSizeString(size: string | undefined) {
const [width, height] = size?.split("x") ?? [undefined, undefined];
return {
height: parseInteger(height),
width: parseInteger(width),
};
}
function parseInteger(value?: string) {
if (value === "" || !value) return undefined;
const number = Number(value);
return Number.isInteger(number) ? number : undefined;
}
async function streamToUint8Array(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
let totalLength = 0;
// Read the stream until it is finished.
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
totalLength += value.length;
}
// Allocate a new Uint8Array to hold all the data.
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}