Skip to content
Trilha

An AI chat inside the app

The chat of the recipe, over the record it is about. The component, the streaming client and the context the page sends are the real ones; only the model's answer is deterministic and runs in the browser, which is why this page needs no API key.

Order #1043

Ada Lovelace

Customer
Ada Lovelace
Status
Shipped
Total
$32.90
Placed
September 1, 2026

Ask anything about this order — where it is, when it arrives, what it cost.

app/api/chat/route.go
// app/api/chat/route.go
func POST(c *trilha.Ctx) error {
	who := AIChatWho(c)
	if ok, after := AIChatLimit.Allow(who); !ok {
		c.Header("Retry-After", strconv.Itoa(after))
		return trilha.Errorf(http.StatusTooManyRequests, "too many questions")
	}
	c.Audit("ai.chat.asked", who)
	return ai.ServeOpts{
		HTML:    ui.ChatHTML,
		Context: AIChatContext,
		Page:    AIChatAnswerPage,
	}.Serve(c, AIChatClient, AIChatAgent())
}

// the page
ui.Chat(c, ui.ChatOpts{
	Action:  "/api/chat",
	History: AIChatLog.Read(AIChatWho(c)),
	Context: map[string]string{"order_id": o.ID},
})
ui.ChatScript(c)

What is pretending

Only the answer. There is no server behind this page: the documentation site is static, and the script answers the same events ai.Serve emits. In an application the form also works with JavaScript off — the same route answers the whole thing at once and the page comes back with the answer in it.

Read the recipe: AI chat