> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat

> Drive the agent with text instead of speech, and exchange real-time messages with clients over room pub/sub.

The pipeline isn't limited to voice. You can feed it **text** and have it reply with text or
speech, and you can exchange **real-time messages** with client apps over the room's pub/sub
channel. Together these power chat widgets, omnichannel agents, and command channels (a
"capture frame" button, a menu choice) alongside the voice conversation.

## Send text in

Push text into the pipeline with `process_text()`: the LLM treats it as if the user said it.
Pair it with a text-capable [pipeline](/build/modalities/text/overview) (`llm` only, or `llm` + `tts`).

<CodeGroup>
  ```python Python theme={null}
  from zrt import Pipeline
  from zrt.plugins import GoogleLLM, CartesiaTTS

  # Text in, voice out
  pipeline = Pipeline(llm=GoogleLLM(), tts=CartesiaTTS())

  # ... when a text message arrives:
  await pipeline.process_text("What are your opening hours?")
  ```
</CodeGroup>

For a pure text chatbot (text in, text out), use an `llm`-only pipeline and read replies from
the `llm` event:

<CodeGroup>
  ```python Python theme={null}
  pipeline = Pipeline(llm=GoogleLLM())

  def on_reply(data):
      print(f"Agent: {data['text']}")

  pipeline.on("llm", on_reply)
  await pipeline.process_text("Hello!")
  ```
</CodeGroup>

## Room pub/sub messaging

Pub/sub lets the agent and your clients exchange messages on named **topics** in the room:
push data to clients, receive commands, or run a side text channel next to the voice call.

### Publish a message

Publish through the session. This works well inside a
`function_tool`, so the LLM itself can send messages:

<CodeGroup>
  ```python Python theme={null}
  from zrt import PubSubPublishConfig

  # Typically called from a function_tool, e.g. self.session inside the agent:
  await self.session.publish_message(
      PubSubPublishConfig(topic="CHAT", message="Hello from the agent")
  )
  ```
</CodeGroup>

### Subscribe to a topic

Register a callback for a topic, typically from `on_enter`, once the session is live:

<CodeGroup>
  ```python Python theme={null}
  def on_message(message):
      print("Received:", message)

  # Inside the agent, e.g. in on_enter: self.session is available on the agent.
  await self.session.subscribe_pubsub("CHAT", on_message)
  ```
</CodeGroup>

A common pattern wires a client message to agent behavior: for example, the client publishes
`"capture_frames"` and the agent responds by capturing [vision](/build/modalities/vision/image-input)
frames, or forwards inbound chat text to `process_text()`.

### Common topics

| Topic        | Used by                                                                |
| :----------- | :--------------------------------------------------------------------- |
| `CHAT`       | App-defined messaging between client and agent.                        |
| `DTMF_EVENT` | Keypad presses, consumed by the [DTMF handler](/build/telephony/dtmf). |

## What's Next

<CardGroup cols={2}>
  <Card title="Transformation" icon="robot" href="/build/modalities/text/transformation">
    Rewrite the text stream before it's spoken
  </Card>

  <Card title="Vision" icon="eye" href="/build/modalities/vision/image-input">
    Trigger frame captures from a client message.
  </Card>
</CardGroup>

## References

<Tabs>
  <Tab title="Python">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Pub/Sub Messaging" icon="github" href="https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples/blob/main/features/pubsub.py">
        Exchange text messages over pub/sub.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
