Multi-tab AI chats

Source: examples/panels/ai/chat_multi_tab.py Test: tests/panels/ai/examples/test_chat_multi_tab.py

Two specialised AI chats, one layout. The left sidebar switches tabs in sync with the main area.

The shape

        graph TB
    subgraph layout [" Panelini "]
        direction LR
        subgraph sidebar [" Sidebar ( pn.Tabs ) "]
            ingest_s(["Ingest AI"])
            digest_s(["Digest AI"])
        end
        subgraph main [" Main ( pn.Tabs ) "]
            ingest_m(["Ingest AI"])
            digest_m(["Digest AI"])
        end
    end

    sidebar -. "jslink active" .-> main
    main -. "jslink active" .-> sidebar

    classDef side fill:#6366f1,stroke:#4f46e5,color:#ffffff
    classDef main fill:#0d7377,stroke:#095c5f,color:#ffffff
    class ingest_s,digest_s side
    class ingest_m,digest_m main
    

Each tab hosts an independent AiChat instance with its own system prompt and welcome message. jslink keeps the active tab indices of the sidebar and main tabs in sync — clicking either one switches both.

The code (condensed)

import panel as pn

from panelini import Panelini
from panelini.panels.ai import AiChat

ingest_ai = AiChat(
    system_message="You are an assistant specialized in data ingestion tasks.",
    welcome_message="Hi! I'm **Ingest AI**. I can help you with data ingestion tasks.",
)

digest_ai = AiChat(
    system_message="You are an assistant specialized in data analysis and summarization.",
    welcome_message="Hi! I'm **Digest AI**. I can help you analyze and summarize data.",
)

main_tabs = pn.Tabs(
    ("Ingest AI", pn.Row(*ingest_ai.main_objects)),
    ("Digest AI", pn.Row(*digest_ai.main_objects)),
)

sidebar_tabs = pn.Tabs(
    ("Ingest AI", pn.Card(*ingest_ai.sidebar_objects, title="Ingest AI Settings")),
    ("Digest AI", pn.Card(*digest_ai.sidebar_objects, title="Digest AI Settings")),
)

main_tabs.jslink(sidebar_tabs, active="active")
sidebar_tabs.jslink(main_tabs, active="active")

app = Panelini(title="AI Chat Multi Tab", sidebar_enabled=True)
app.main_set(objects=[main_tabs])
app.sidebar_set(objects=[sidebar_tabs])

Why two AiChat instances?

Each AiChat carries its own:

  • conversation history

  • provider / model / temperature selection

  • tool set

  • preview pane

That’s exactly the isolation you want for domain-specialised assistants — the Ingest bot’s conversation can’t leak into Digest’s context.

See also