# Coco App chat_chat Process: How Messages Flow from React UI to AI Response

> Discover how the Coco App chat_chat command sends React UI messages to the Rust backend and AI responses from the Coco server. Explore the four-layer pipeline and event listener streaming.

- Repository: [INFINI Labs/coco-app](https://github.com/infinilabs/coco-app)
- Tags: internals
- Published: 2026-03-04

---

**The `chat_chat` command in Coco App orchestrates a four-layer pipeline that transports user prompts from the React frontend through a Rust backend to the remote Coco server, then streams AI responses back via event listeners.**

The `chat_chat` process powers real-time AI conversations in the **infinilabs/coco-app** repository, bridging the React-based user interface with backend streaming endpoints. This technical deep dive examines how the system handles message transmission, distinguishes between desktop and web transports, and manages server-sent event streams using cross-platform adapters.

## The Four-Layer Message Architecture

The complete round-trip consists of four distinct layers that handle serialization, transport, and streaming. Each layer corresponds to specific source files in the codebase:

- **Layer 1: UI Interaction** – [`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts) captures user input via `handleSendMessage` and initializes the chat session
- **Layer 2: Platform Abstraction** – [`src/utils/platformAdapter.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/platformAdapter.ts) routes commands to Tauri or web implementations based on the runtime environment
- **Layer 3: Rust Command Bridge** – [`src-tauri/src/assistant/mod.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/assistant/mod.rs) executes HTTP requests to the Coco server using `HttpClient::advanced_post`
- **Layer 4: Event Streaming** – Event listeners in [`useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/useChatActions.ts) process the byte-stream response via `handleChatCreateStreamMessage`

## Step 1: Initiating Messages with handleSendMessage

When a user submits a prompt, the UI invokes `handleSendMessage` from [`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts) (lines 73-80). This hook first validates that the active chat session exists, then delegates to the internal `sendMessage` function while passing the message payload and attachments.

```typescript
import { useChatActions } from '@/hooks/useChatActions';

export default function ChatInput({ activeChat }) {
  const { handleSendMessage } = useChatActions();

  const onSubmit = async (text: string) => {
    await handleSendMessage(activeChat, { message: text });
  };

  return (
    <input
      placeholder="Ask Coco…"
      onKeyDown={e => e.key === 'Enter' && onSubmit(e.currentTarget.value)}
    />
  );
}

```

The `handleSendMessage` wrapper ensures the chat history is initialized before triggering the transport layer, preventing orphaned messages in the UI state.

## Step 2: Platform Adapter Routing

The `sendMessage` function (lines 31-44 in [`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts)) constructs a unique **client-id** using the pattern `chat-stream-${clientId}-${timestamp}` and determines the execution path based on the runtime environment. This client ID becomes the correlation key for matching responses to requests.

### Desktop: Tauri Command Invocation

For desktop applications, the code invokes `platformAdapter.commands("chat_chat", ...)`, which routes to the Tauri Rust backend:

```typescript
const sendMessage = useCallback(
  async (newChat, params) => {
    const timestamp = Date.now();
    const clientId = `chat-stream-${clientId}-${timestamp}`;

    const queryParams = { /* query configuration */ };
    await platformAdapter.commands('chat_chat', {
      serverId: currentService?.id,
      sessionId: newChat._id,
      queryParams,
      message: params.message,
      attachments: params.attachments,
      clientId,
    });
    resetChatState();
  },
  [/* deps */],
);

```

### Web: Direct Stream Fetching

In browser environments, the same hook calls `streamPost` from [`src/api/streamFetch.ts`](https://github.com/infinilabs/coco-app/blob/main/src/api/streamFetch.ts) (lines 1-60), which uses the native `fetch` API with `ReadableStream` handling to consume server-sent events directly without Tauri mediation. This web fallback maintains identical logical steps while bypassing the Rust command layer.

## Step 3: Rust Backend Processing

For desktop clients, `platformAdapter.commands` resolves to the **`chat_chat`** Tauri command defined in [`src-tauri/src/assistant/mod.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/assistant/mod.rs) (lines 76-84). This Rust function serializes the payload and establishes a streaming HTTP connection to the remote Coco server via `HttpClient::advanced_post`.

```rust
#[tauri::command]
pub async fn chat_chat(
    app_handle: AppHandle,
    server_id: String,
    session_id: String,
    message: Option<String>,
    attachments: Option<Vec<String>>,
    query_params: Option<HashMap<String, Value>>,
    client_id: String,
) -> Result<(), String> {
    // Build request body
    let request_message = ChatRequestMessage { message, attachments };
    let body = Some(serde_json::to_string(&request_message)?.into());

    // POST to the remote Coco server, receive a streaming response
    let response = HttpClient::advanced_post(
        &server_id,
        &format!("/chat/{}/_chat", session_id),
        None,
        convert_query_params_to_strings(query_params),
        body,
    )
    .await?;

    // Forward each line to the front-end
    let mut lines = tokio::io::BufReader::new(
        tokio_util::io::StreamReader::new(
            response.bytes_stream().map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
        )
    )
    .lines();

    while let Ok(Some(line)) = lines.next_line().await {
        app_handle.emit(&client_id, line).ok();
    }
    Ok(())
}

```

The command POSTs to `/chat/{session_id}/_chat`, then reads the response byte-stream line-by-line using `tokio::io::BufReader`, emitting each line to the frontend using the supplied `client_id` as the event identifier.

## Step 4: Listening for Streamed Responses

Before invoking the command, the React hook registers an event listener using the matching `client_id`. In [`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts) (lines 257-264), `platformAdapter.listenEvent` captures each emitted line and processes it through `handleChatCreateStreamMessage` to update the chat view in real-time.

```typescript
useEffect(() => {
  const clientId = `chat-stream-${clientId}-${timestamp}`;
  const unlisten = await platformAdapter.listenEvent(clientId, (event) => {
    handleChatCreateStreamMessage(event.payload);
  });
  return () => unlisten();
}, [clientId, timestamp]);

```

This listener appends each chunk to the chat view as it arrives, creating the streaming effect users experience while the AI generates responses.

## Key Source Files in the chat_chat Flow

Understanding these files provides complete visibility into the message pipeline according to the infinilabs/coco-app source code:

- **[`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts)** – Contains `handleSendMessage`, `sendMessage`, and event listener registration logic for both desktop and web paths
- **[`src-tauri/src/assistant/mod.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/assistant/mod.rs)** – Implements the Rust `chat_chat` command that communicates with the Coco server via `HttpClient::advanced_post`
- **[`src/utils/platformAdapter.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/platformAdapter.ts)** – Abstracts platform-specific command execution, routing to Tauri or web implementations
- **[`src/api/streamFetch.ts`](https://github.com/infinilabs/coco-app/blob/main/src/api/streamFetch.ts)** – Web-only implementation using native fetch streams for browser environments
- **[`src/utils/tauriAdapter.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/tauriAdapter.ts)** – Tauri-specific wrapper forwarding JavaScript commands to the Rust backend

## Summary

The `chat_chat` process in Coco App implements a sophisticated cross-platform messaging pipeline:

- **Initialization** occurs through `handleSendMessage` in the React hooks layer, ensuring session validity before transport via [`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts)
- **Transport abstraction** routes desktop requests through Tauri's Rust bridge while web clients use native fetch streams in [`src/api/streamFetch.ts`](https://github.com/infinilabs/coco-app/blob/main/src/api/streamFetch.ts)
- **Rust command execution** handles HTTP POST requests to `/chat/<session>/_chat` and manages byte-stream reading via `HttpClient::advanced_post` in [`src-tauri/src/assistant/mod.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/assistant/mod.rs)
- **Event-driven responses** stream back to the UI using client-specific identifiers, with listeners in [`useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/useChatActions.ts) processing each chunk via `handleChatCreateStreamMessage`

## Frequently Asked Questions

### What is the purpose of the client_id in the chat_chat process?

The **client_id** (formatted as `chat-stream-${clientId}-${timestamp}`) serves as a unique correlation identifier that matches the Rust backend's event emissions with the correct frontend event listener. According to the source code in [`src/hooks/useChatActions.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useChatActions.ts), this ID ensures that streaming responses from multiple concurrent chat sessions route to their respective UI components without collision by acting as the event channel name.

### How does Coco App handle chat messaging in web browsers versus desktop applications?

The application uses conditional transport logic within `sendMessage`. **Desktop** builds invoke `platformAdapter.commands("chat_chat", ...)` which triggers the Tauri Rust command in [`src-tauri/src/assistant/mod.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/assistant/mod.rs). **Web** builds call `streamPost` from [`src/api/streamFetch.ts`](https://github.com/infinilabs/coco-app/blob/main/src/api/streamFetch.ts), which uses the browser's native `fetch` API with `ReadableStream` to consume the same Coco server endpoint directly, bypassing the Rust layer while maintaining identical streaming behavior.

### Which Rust function actually sends the HTTP request to the Coco server?

The **`chat_chat`** function in [`src-tauri/src/assistant/mod.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/assistant/mod.rs) (lines 76-84) creates the HTTP request using `HttpClient::advanced_post`. This function serializes the message and attachments into a `ChatRequestMessage`, POSTs to the `/chat/{session_id}/_chat` endpoint, and then streams the response body line-by-line back to the JavaScript frontend via Tauri's `app_handle.emit` event system.

### Where does the streaming response get parsed and displayed in the UI?

The React hook `useChatActions` registers an event listener using `platformAdapter.listenEvent` with the specific `client_id` before sending the message. When the Rust backend emits lines via `app_handle.emit(&client_id, line)`, this listener receives the payload and passes it to `handleChatCreateStreamMessage`, which appends the content to the active chat view in real-time, creating the streaming text effect.