> ## 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 live session with `session.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 zeroruntime import Pipeline
  from zeroruntime.plugins import GoogleLLM, CartesiaTTS

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

  # ... when a text message arrives, feed it to the live session:
  await self.session.process_text("What are your opening hours?")
  ```

  ```typescript Node JS theme={null}
  import { Pipeline } from '@zeroruntime/js-sdk';
  import { GoogleLLM, CartesiaTTS } from '@zeroruntime/js-sdk/plugins';

  // Text in, voice out
  const pipeline = Pipeline({ llm: GoogleLLM(), tts: CartesiaTTS() });

  // ... when a text message arrives, feed it to the live session:
  await this.session!.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)

  # `process_text` lives on the live session, not on the pipeline:
  await session.process_text("Hello!")
  ```

  ```typescript Node JS theme={null}
  const pipeline = Pipeline({ llm: GoogleLLM() });

  function on_reply(data) {
    console.log(`Agent: ${data['text']}`);
  }

  pipeline.on('llm', on_reply);

  // `process_text` lives on the live session, not on the pipeline:
  await session.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 zeroruntime import PubSubPublishConfig

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

  ```typescript Node JS theme={null}
  import { PubSubPublishConfig } from '@zeroruntime/js-sdk';

  // Typically called from a function_tool, e.g. this.session inside the agent:
  await this.session!.publish_to_pubsub(
    PubSubPublishConfig({ topic: 'CHAT', message: 'Hello from the agent' }),
  );
  ```
</CodeGroup>

### Subscribe to a topic

Subscribe from `on_enter`, once the session exists, and hand it the method that
should receive each frame:

<CodeGroup>
  ```python Python theme={null}
  from zeroruntime import Agent, PubSubSubscribeConfig

  class ChatAgent(Agent):
      async def on_enter(self) -> None:
          await self.session.subscribe_to_pubsub(
              PubSubSubscribeConfig(topic="CHAT", cb=self.on_chat)
          )

      async def on_chat(self, frame: dict, backlog: bool) -> None:
          # Subscribing replays the topic's history, so `backlog` is what
          # separates it from anything sent since -- usually worth skipping.
          if backlog:
              return
          print("Received:", frame.get("message"))
  ```

  ```typescript Node JS theme={null}
  import { Agent, PubSubSubscribeConfig } from '@zeroruntime/js-sdk';

  class ChatAgent extends Agent {
    async on_enter(): Promise<void> {
      await this.session!.subscribe_to_pubsub(
        PubSubSubscribeConfig({ topic: 'CHAT', cb: this.on_chat.bind(this) }),
      );
    }

    async on_chat(frame: Record<string, any>, backlog: boolean): Promise<void> {
      // Subscribing replays the topic's history, so `backlog` is what
      // separates it from anything sent since -- usually worth skipping.
      if (backlog) {
        return;
      }
      console.log('Received:', frame.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/zeroruntime-python-examples/blob/main/getting_started/chat_agent.py">
        Exchange text messages over pub/sub.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Node JS">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Pub/Sub Messaging" icon="github" href="https://github.com/ZeroRuntimeAI/zeroruntime-js-examples/blob/main/getting_started/chat_agent.ts">
        Exchange text messages over pub/sub.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
