调用Google Vertex AI多模态嵌入API遇500错误求排查
问题:调用Vertex AI多模态嵌入模型返回500错误,疑为图片编码问题
最终目标:获取Embeddings并存储至向量数据库
我在Next.js API路由(Node.js环境)中,通过导出的POST函数处理POST请求,尝试调用Vertex AI的多模态嵌入模型。
后端代码(Next.js API路由)
import { UploadImageResponseBody } from "@/types"; /* 本来打算用这个客户端库重构,但文档看得一头雾水, 如果用这个客户端调用同个模型更简单的话,欢迎给示例 */ import { PredictionServiceClient } from "@google-cloud/aiplatform"; /** * 虽然项目在`us-east1`,但只有`us-central1`能正常工作 */ const LOCATION = "us-central1"; const PROJECT_ID = "..."; const MODEL = "multimodalembedding"; const RPC_CALL = "predict"; const G_CLOUD_ACCESS_TOKEN ="..."; const REQUEST_URL = `https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/publishers/google/models/${MODEL}@001:${RPC_CALL}`; type Instances = { image: { bytesBase64Encoded: string } }[]; async function getEmbedding(instances: Instances) { try { const response = await fetch(REQUEST_URL, { method: "POST", headers: { Authorization: `Bearer ${G_CLOUD_ACCESS_TOKEN}`, }, body: JSON.stringify({ instances, }), }); return response; } catch (error) { console.error(error); return null; } } export const POST = async (req: Request) => { const formData = await req.formData(); const imageBlobs = formData.getAll("images") as Blob[]; const base64Images = await Promise.allSettled( imageBlobs.map(async (imgBlob) => { const binaryArr = await imgBlob.arrayBuffer(); const buffer = Buffer.from(binaryArr); const base64EncodedImage = buffer.toString("base64"); const fileName = (imgBlob as File).name; return { base64EncodedImage, fileName }; }) ); const successfulEncodings = base64Images.filter( (imgRes) => imgRes.status === "fulfilled" ) as PromiseFulfilledResult<{ base64EncodedImage: string; fileName: string; }>[]; const instances = successfulEncodings.map((imgRes) => { return { image: { bytesBase64Encoded: imgRes.value.base64EncodedImage, }, }; }); const embeddings = await getEmbedding(instances); // ...后续逻辑 return response; };
收到的错误响应
{ embeddings: Response { [Symbol(realm)]: null, [Symbol(state)]: { aborted: false, rangeRequested: false, timingAllowPassed: true, requestIncludesCredentials: true, type: 'default', status: 500, timingInfo: [Object], cacheState: '', statusText: 'Internal Server Error', headersList: [HeadersList], urlList: [Array], body: [Object] }, [Symbol(headers)]: HeadersList { [Symbol(headers map)]: [Map], [Symbol(headers map sorted)]: null } } } // 以下是我自己加的日志,看到timingAllowPassed为true所以记录了耗时 getEmbeddingTime: 50.414s
之前的文本嵌入功能完全正常,所以我怀疑问题出在图片的处理/编码环节。我知道500内部服务器错误通常不是客户端的问题,但还是觉得可能是自己操作有误。
前端上传代码参考
"use client"; import { ChangeEvent, useState, EventHandler, MouseEvent } from "react"; import { Button } from "../Button"; import axios from "axios"; import ImageFileList from "./ImageFileList"; export const FileUpload: React.FC = () => { const [selectedImageFiles, setSelectedImageFiles] = useState<File[]>([]); const handleFileChange: EventHandler<ChangeEvent<HTMLInputElement>> = ( event ) => { const { files } = event.target; if (!files || !files.length) return; const fileArr = Array.from(files); const MAX_BYTES_2MB = 2_000_000; for (const f of fileArr) { if (f.size > MAX_BYTES_2MB) { const warning = `File ${f.name} is too large. Please upload a file less than 2MB`; alert(warning); } } const acceptedFiles = fileArr.filter((f) => f.size <= MAX_BYTES_2MB); const newFiles = [...selectedImageFiles, ...acceptedFiles]; setSelectedImageFiles(newFiles); }; const handleUploadImagesClick: EventHandler< MouseEvent<HTMLButtonElement> > = async () => { if (selectedImageFiles.length === 0) { alert("Please select some images to upload"); return; } const formData = new FormData(); for (let i = 0; i < selectedImageFiles.length; i++) { formData.append("images", selectedImageFiles[i]); } try { const response = await axios.postForm("/api/images", formData); } catch (error) { console.error(error); } }; const hasFilesSelected = !!selectedImageFiles && selectedImageFiles.length > 0; return ( <div > <label > <span> {hasFilesSelected ? ( <ImageFileList files={selectedImageFiles} /> ) : ( "Upload Images" )} </span> <input type="file" onChange={handleFileChange} accept=".jpg,.jpeg,.png" /> </label> <Button onClick={handleUploadImagesClick} disabled={!hasFilesSelected} > Upload </Button> </div> ); };
内容的提问来源于stack exchange,提问作者Preston Bourne
相关产品推荐
相关产品推荐

