惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
雷峰网
雷峰网
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
量子位
有赞技术团队
有赞技术团队
博客园 - 三生石上(FineUI控件)
博客园 - Franky
M
MIT News - Artificial intelligence
U
Unit 42
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
J
Java Code Geeks
V
Visual Studio Blog
Engineering at Meta
Engineering at Meta
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
MyScale Blog
MyScale Blog
T
Tailwind CSS Blog
T
The Blog of Author Tim Ferriss
V
V2EX

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Streaming Gemini API Responses in Rust + Tauri — Real-Tim...
hiyoyo · 2026-05-04 · via DEV Community
Cover image for Streaming Gemini API Responses in Rust + Tauri — Real-Time Token Display

hiyoyo

If this is useful, a ❤️ helps others find it.

All tests run on an 8-year-old MacBook Air.

Waiting 5 seconds for an AI response with no feedback feels broken. Streaming fixes this — tokens appear as they're generated, just like ChatGPT.

Here's how to wire Gemini streaming into a Tauri app so the response appears word by word in your UI.


The API endpoint

Replace :generateContent with :streamGenerateContent:

POST /v1beta/models/gemini-2.5-flash-preview:streamGenerateContent?key=API_KEY

Enter fullscreen mode Exit fullscreen mode

The response is a stream of JSON objects, one per token batch.


Rust: reading the stream

use reqwest::Client;
use futures_util::StreamExt;
use tauri::Emitter;

#[tauri::command]
pub async fn stream_gemini(
    prompt: String,
    api_key: String,
    window: tauri::Window,
) -> Result<(), String> {
    let client = Client::new();
    let url = format!(
        "https://generativelanguage.googleapis.com/v1beta/models/\
         gemini-2.5-flash-preview:streamGenerateContent?key={}",
        api_key
    );

    let body = serde_json::json!({
        "contents": [{"parts": [{"text": prompt}]}]
    });

    let mut stream = client
        .post(&url)
        .json(&body)
        .send()
        .await
        .map_err(|e| e.to_string())?
        .bytes_stream();

    while let Some(chunk) = stream.next().await {
        let bytes = chunk.map_err(|e| e.to_string())?;
        let text = String::from_utf8_lossy(&bytes);

        // Each chunk is a JSON object — extract the text
        if let Ok(json) = serde_json::from_str::(&text) {
            if let Some(token) = json["candidates"][0]["content"]["parts"][0]["text"].as_str() {
                // Emit each token to the frontend
                window.emit("ai-token", token).ok();
            }
        }
    }

    window.emit("ai-done", ()).ok();
    Ok(())
}

Enter fullscreen mode Exit fullscreen mode


React: receiving tokens

import { listen } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api/core';
import { useState, useEffect, useRef } from 'react';

export function StreamingDiagnosis() {
  const [response, setResponse] = useState('');
  const [isStreaming, setIsStreaming] = useState(false);
  const unlistenRef = useRef<(() => void) | null>(null);

  const startStream = async (prompt: string) => {
    setResponse('');
    setIsStreaming(true);

    // Listen for tokens
    unlistenRef.current = await listen('ai-token', (event) => {
      setResponse(prev => prev + event.payload);
    });

    // Listen for completion
    const unlistenDone = await listen('ai-done', () => {
      setIsStreaming(false);
      unlistenDone();
    });

    await invoke('stream_gemini', { prompt, apiKey: 'YOUR_KEY' });
  };

  // Cleanup on unmount
  useEffect(() => {
    return () => { unlistenRef.current?.(); };
  }, []);

  return (



{response}{isStreaming && }

       startStream('Analyze this error...')}>
        Diagnose



  );
}

Enter fullscreen mode Exit fullscreen mode


The blinking cursor

A small CSS detail that makes streaming feel polished:

.cursor {
  animation: blink 1s step-end infinite;
}

@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

Enter fullscreen mode Exit fullscreen mode

Appears while streaming, disappears when ai-done fires.


Result

5-second wait with no feedback → tokens appearing immediately, word by word. Same content, completely different feel.


Hiyoko PDF Vault → https://hiyokoko.gumroad.com/l/HiyokoPDFVault
X → @hiyoyok