cd /news/artificial-intelligence/your-gmail-can-write-code · home topics artificial-intelligence article
[ARTICLE · art-116267] src=dejan.ai ↗ pub= topic=artificial-intelligence verified=true sentiment=↑ positive

Your 'Gmail' can write code.

Google's Gemini AI in Gmail drafted a fully functional Python script to fine-tune Microsoft's DeBERTa-v3-large model for 3-label sentiment classification, including data loading, training loops, and evaluation, demonstrating the AI's capability to generate complex code within email drafts.

read3 min views1 publishedAug 31, 2026
Your 'Gmail' can write code.
Image: Dejan (auto-discovered)

Gemini in Gmail drafted Python code to fine-tune a DeBERTa model for sentiment classification, complete with data , training loops, and evaluation.

Gmail drafter is powered by a language model, and it can do almost anything your chat assistant can. For example here I ask it to write python code to train a sentiment classification model.

write code for train.py to fine-tune deberta v3 large for sentiment classification into 3 labels: positive, neutral and negative data.csv as source headers: text,positive,neutral,negative (values for labels are 0 or 1) use tqdm and wandb for logging per step

It drafted the following email with a fully functional python code:


Hey [Name],

Here is the code for train.py to fine-tune DeBERTa-v3-large on your sentiment data. It reads from data.csv, sets up a 3-label classification, and handles logging via tqdm and Weights & Biases per step.

import pandas as pd
import torch
from torch.utils.data import Dataset, Data
from transformers import AutoTokenizer, AutoModelForSequenceClassification, AdamW, get_scheduler
from sklearn.model_selection import train_test_split
import wandb
from tqdm import auto as tqdm_auto
import numpy as np

wandb.init(project="deberta-sentiment-classification")

df = pd.read_csv('data.csv')

def get_label(row):
    if row['positive'] == 1: return 0
    if row['neutral'] == 1: return 1
    if row['negative'] == 1: return 2
    return -1

df['label'] = df.apply(get_label, axis=1)
df = df[df['label'] != -1].reset_index(drop=True)

train_df, val_df = train_test_split(df, test_size=0.1, random_state=42)

class SentimentDataset(Dataset):
    def __init__(self, texts, labels, tokenizer, max_len=128):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len
       
    def __len__(self):
        return len(self.texts)
   
    def __getitem__(self, idx):
        text = str(self.texts[idx])
        label = self.labels[idx]
        encoding = self.tokenizer(
            text,
            add_special_tokens=True,
            max_length=self.max_len,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': torch.tensor(label, dtype=torch.long)
        }

MODEL_NAME = "microsoft/deberta-v3-large"
tokenizer = AutoTokenizer.pretrained(MODEL_NAME)
model = AutoModelForSequenceClassification.pretrained(MODEL_NAME, num_labels=3)

train_dataset = SentimentDataset(train_df['text'].values, train_df['label'].values, tokenizer)
val_dataset = SentimentDataset(val_df['text'].values, val_df['label'].values, tokenizer)

train_ = Data(train_dataset, batch_size=8, shuffle=True)
val_ = Data(val_dataset, batch_size=16)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

optimizer = AdamW(model.parameters(), lr=2e-5)
num_epochs = 3
num_training_steps = num_epochs * len(train_)
lr_scheduler = get_scheduler("linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps)

wandb.config.update({
    "model": MODEL_NAME,
    "epochs": num_epochs,
    "batch_size": 8,
    "lr": 2e-5
})

global_step = 0
for epoch in range(num_epochs):
    model.train()
    train_bar = tqdm_auto.tqdm(train_, desc=f"Epoch {epoch+1}/{num_epochs} [Train]")
   
    for batch in train_bar:
        optimizer.zero_grad()
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['labels'].to(device)
       
        outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
        loss = outputs.loss
        loss.backward()
       
        optimizer.step()
        lr_scheduler.step()
       
        global_step += 1
        wandb.log({"train_loss": loss.item(), "learning_rate": lr_scheduler.get_last_lr()[0]}, step=global_step)
        train_bar.set_postfix(loss=loss.item())

    model.eval()
    val_loss = 0
    correct = 0
    total = 0
    val_bar = tqdm_auto.tqdm(val_, desc=f"Epoch {epoch+1}/{num_epochs} [Val]")
   
    with torch.no_grad():
        for batch in val_bar:
            input_ids = batch['input_ids'].to(device)
            attention_mask = batch['attention_mask'].to(device)
            labels = batch['labels'].to(device)
           
            outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
            val_loss += outputs.loss.item()
           
            preds = torch.argmax(outputs.logits, dim=1)
            correct += (preds == labels).sum().item()
            total += labels.size(0)

    avg_val_loss = val_loss / len(val_)
    accuracy = correct / total
    wandb.log({"val_loss": avg_val_loss, "val_accuracy": accuracy}, step=global_step)
    print(f"Epoch {epoch+1} - Val Loss: {avg_val_loss:.4f}, Val Acc: {accuracy:.4f}")

model.save_pretrained("./sentiment_model")
tokenizer.save_pretrained("./sentiment_model")
wandb.finish()

Let me know if you need to adjust the batch size or max length constraints.


<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 200" width="100%" height="100%">
  <defs>
    <!-- Clean, modern linear gradient for the logo mark -->
    <linearGradient id="dejanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
      <stop offset="0%" stop-color="#0052CC" />
      <stop offset="100%" stop-color="#00A3BF" />
    </linearGradient>
  </defs>

  <g transform="translate(10, 10)">
    <!-- Placeholder for the DEJAN geometric icon/logo mark -->
    <path d="M 50 25 L 125 25 L 150 100 L 125 175 L 50 175 L 25 100 Z" fill="url(#dejanGrad)" />
    <path d="M 65 50 L 110 50 L 125 100 L 110 150 L 65 150 Z" fill="#FFFFFF" />
   
    <!-- DEJAN Typography -->
    <text x="190" y="135" font-family="Arial, Helvetica, sans-serif" font-size="90" font-weight="900" fill="#172B4D" letter-spacing="2">DEJAN</text>
  </g>
</svg>

PS: Cute, but I think I'll keep the one we have for now though ;-)

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @google 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/your-gmail-can-write…] indexed:0 read:3min 2026-08-31 ·