Vertex batch embeddings (gemini-embedding-001): no way to attach billing labels; job and row labels never reach Cloud Billing
Los mantenedores suelen responder en 1 día
@Venkaiahbabuneelam ya está trabajando en esto.
Desde el 5/10/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
Billing labels on Vertex AI batch embedding jobs never reach Cloud Billing
Environment: google-genai 2.22.0, Python 3.13, Vertex AI (vertexai=True), us-central1, model gemini-embedding-001
What you're trying to do
We split our Vertex AI costs by custom metadata labels.
- Real-time calls work:
generateContentwithGenerateContentConfig(labels=...), andgemini-embedding-001embed_content(which the SDK sends to:predict) with labels added throughhttp_options.extra_body. Both labels show up in Cloud Billing. - Batch embeddings don't: for the same model through
client.batches.create(...), no label has ever appeared in billing, whether set on the job or in the JSONL rows.
Questions:
- Is there a supported way to attach billing labels to a Vertex batch embedding job?
- Batch rows accept
request.labels, while every other unknown key fails the row. Is that intended, and should it reach billing? - Could
CreateBatchJobConfigandEmbedContentConfigget alabelsfield, asGenerateContentConfigandGenerateImagesConfighave? CouldBatchJobkeeplabelswhen reading a job back? Today it drops them.
What code you've already tried
1. Real-time embedding: works
The label shows in billing.
await client.aio.models.embed_content(
model='gemini-embedding-001',
contents='...',
config=types.EmbedContentConfig(
task_type='RETRIEVAL_DOCUMENT',
output_dimensionality=768,
http_options=types.HttpOptions(extra_body={'labels': {'testing': 'rt_predict'}}),
),
)
Request as sent (intercepted):
POST https://us-central1-aiplatform.googleapis.com/v1beta1/projects/PROJECT/locations/us-central1/publishers/google/models/gemini-embedding-001:predict
{"instances": [{"content": "...", "task_type": "RETRIEVAL_DOCUMENT"}],
"parameters": {"outputDimensionality": 768},
"labels": {"testing": "rt_predict"}}
-> 200
Note:
PredictRequest.labelsis documented as "for Imagen billing usage only", but these labels do reach billing for this model.
2. Batch job labels: the job stores them, billing doesn't show them
CreateBatchJobConfig has no labels field, so we add them through extra_body:
await client.aio.batches.create(
model='gemini-embedding-001',
src=types.BatchJobSource(gcs_uri=['gs://BUCKET/input.jsonl'], format='jsonl'),
config=types.CreateBatchJobConfig(
display_name='label_test',
dest='gs://BUCKET/outputs/',
http_options=types.HttpOptions(extra_body={'labels': {'testing': 'j_labels'}}),
),
)
The job is created, a REST GET on the job returns "labels": {"testing": "j_labels"}, and the rows embed fine. No testing label appears on the cost.
3. Per-row labels
We tried every placement we could think of, one job per placement. Rows look like this:
{"key": "1", "request": {"content": {"parts": [{"text": "..."}]}, "output_dimensionality": 768, "task_type": "RETRIEVAL_DOCUMENT", "labels": {"testing": "r_request"}}}
| Label placement in the JSONL line | Result |
|---|---|
request.labels |
row embeds fine (accepted), not in billing |
request.content.labels |
row error: no such field: 'labels' (Content) |
request.content.parts[0].labels |
row error: no such field: 'labels' (Part) |
request.embed_content_config.labels / request.embedContentConfig.labels |
row error: no such field: 'labels' (EmbedContentConfig) |
request.metadata.labels |
row error: no such field: 'metadata' |
request.request_labels |
row error: no such field: 'request_labels' |
labels object beside request |
job fails (see errors below) |
labels as a JSON string beside request |
accepted as a pass-through column, not in billing |
flat request.content string (:predict-style) |
row error: can't parse into EmbedContentRequest |
Job-level placements:
| Placement on the job | Result |
|---|---|
labels |
stored on the job, not in billing |
modelParameters.labels |
stored, not in billing |
inputConfig.labels, outputConfig.labels, instanceConfig.labels |
400 at creation |
4. Real-time embedContent with labels
This is the request type batch rows are parsed into. It is rejected on both v1 and v1beta1:
POST .../publishers/google/models/gemini-embedding-001:embedContent
{"content": {"parts": [{"text": "..."}]}, "taskType": "RETRIEVAL_DOCUMENT", "outputDimensionality": 768, "labels": {"testing": "rt_embed_content"}}
-> 400
Any error messages you're getting
No error where it matters: request.labels and job labels are accepted and the jobs succeed, but the labels never reach Cloud Billing.
Errors from the other placements:
Failed to parse JSON into proto: google.cloud.aiplatform.master.EmbedContentRequest with status: invalid JSON in google.cloud.aiplatform.master.EmbedContentRequest @ content: message google.cloud.aiplatform.master.Content, near 1:22 (offset 21): no such field: 'labels'
Failed to parse JSON into proto: google.cloud.aiplatform.master.EmbedContentRequest with status: invalid JSON in google.cloud.aiplatform.master.EmbedContentRequest, near 1:86 (offset 85): no such field: 'metadata'
The column or property "labels" in the specified input data is of unsupported type. Supported types for this column are: [STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, DATE, DATETIME, NUMERIC].
400 INVALID_ARGUMENT. Invalid JSON payload received. Unknown name "labels" at 'batch_prediction_job.input_config': Cannot find field.
400 INVALID_ARGUMENT. Invalid JSON payload received. Unknown name "labels": Cannot find field. (real-time :embedContent, v1 and v1beta1)
- Lenguaje dominante
- Python
- Estrellas
- 4k
- Forks
- 1k
- Merge medio
- 1 d 17 h
- PR fusionados (30 d)
- 54
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de googleapis/python-genai
-
Curated history keeps half a user turn when the model turn is invalidPosiblemente ocupada @Venkaiahbabuneelam la tomó hace 2 días. Abiertopriority: p2 status:awaiting user response type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
googleapis/python-genai#3051 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
[Bug]: Unsubscripted typing.List and typing.Dict crash convert_if_exist_pydantic_model in AFCPosiblemente ocupada @chauvuusvn la tomó hace 4 días. Abiertopriority: p2 type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
googleapis/python-genai#3044 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Name collision in google.genai.interactions: triggers.Interaction shadows response model Interaction in static type checkersPosiblemente ocupada @Venkaiahbabuneelam la tomó hace 9 días. Abiertopriority: p2 type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
googleapis/python-genai#3013 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Callable tools: optional scalar parameters (`int | None`) get `"type": "object"` in `parameters_json_schema`, so Gemini sends `{}`Posiblemente ocupada @Venkaiahbabuneelam la tomó hoy. Abiertopriority: p2 type: bug
googleapis/python-genai#3060 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Gemini API latency has recently increased significantly, especially with store=truePosiblemente ocupada @Venkaiahbabuneelam la tomó hoy. Abiertopriority: p3 status:awaiting user response type: question
googleapis/python-genai#3059 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de googleapis/python-genai
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
fabriziosalmi/certmate#1207 ·
Los mantenedores suelen responder en 1 día
-
[work-item] adam-adae-onset-emergence: document the minute-precision datetime coercion (ASTTMF not derivable)Posiblemente ocupada @muse-yamaa-bot la tomó hoy. Abiertowork-item
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
CommunityToolkit/Aspire#2231 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
volcengine/OpenViking#5711 ·
Los mantenedores suelen responder en 1 día