AI replies
When a user chats one-to-one with an AI bot, the bot's reply is a message of type ai whose content is pushed while it is being generated. The UniApp SDK's client.ai is the same as the Web SDK's: it joins live content in order, resumes after disconnects and fills in after reconnecting, and once the reply ends the message body is authoritative. The APIs, fields and events are described fully in the Web SDK's AI replies; this page covers displaying them in uni-app pages. Bots and replies are created by your server, see AI conversations.
Version
AI replies are available from @deeprespond/im-uniapp@1.0.6 (which depends on @deeprespond/im-web@1.0.6); see Integration and downloads to install.
Turn it into a view
Mini programs and apps cannot render Markdown directly. aiView(message, live) from @deeprespond/im-web/render (installed with @deeprespond/im-uniapp) turns an AI message into a view the template can use as is, converting the body into a rich-text node array (it only produces nodes, never HTML strings; links keep only their text). The view's fields are listed in the mini program SDK's AI replies.
<template>
<view class="ai-msg" :data-status="view.status">
<text v-if="view.reasoning">{{ view.thinking ? '思考中…' : '已深度思考' }}</text>
<text v-for="tool in view.tools" :key="tool.id">{{ tool.title }}({{ tool.state }})</text>
<view v-for="(block, index) in view.blocks" :key="index">
<view v-if="block.kind === 'text'">
<rich-text :nodes="block.nodes" selectable />
<text v-if="view.streaming && block.last">▍</text>
</view>
<image v-else-if="block.kind === 'image'" :src="block.url" mode="widthFix" />
<text v-else-if="block.kind === 'file'">{{ block.name }} {{ block.size }}</text>
</view>
<text v-if="view.status === 'stopped'">已停止生成</text>
<text v-if="view.status === 'failed'">生成中断:{{ view.error }}</text>
<text v-if="view.streaming" @click="stop">停止生成</text>
</view>
</template>
<script>
import { aiView } from '@deeprespond/im-web/render';
export default {
props: { item: Object, client: Object },
data: () => ({ live: null, offs: [] }),
computed: {
streamId() { return this.item.body?.stream_id; },
// 生成中取实时内容,结束后取消息正文
view() { return aiView(this.item, this.item.body?.status === 'streaming' ? this.live : null); },
},
mounted() {
this.live = this.client.ai.state(this.streamId);
const mine = (fn) => (s) => { if (s.stream_id === this.streamId) fn(s); };
this.offs = [
this.client.on('ai.streamStarted', mine((s) => (this.live = s))),
this.client.on('ai.streamUpdated', mine((s) => (this.live = s))),
this.client.on('ai.streamEnded', mine(() => (this.live = null))),
];
},
beforeUnmount() { this.offs.forEach((off) => off()); },
methods: {
stop() { this.client.ai.stop(this.streamId); },
},
};
</script>- The
partsofai.streamUpdatedare the full current content, so redraw just this message from them; pushes are merged into one every 50 ms or 2 KB. - After the reply ends, the message list updates with the final content and
client.ai.state()returnsnull. - Stop generating: only the user receiving the reply can stop it; stopping a reply that already ended returns its current state without an error.
- Regenerate: send the answered question again and the bot's server starts a new reply.
- For conversation previews, use the SDK's
summarize(which supports AI messages) or the body'stext.
On H5, where HTML can be rendered directly, you can also take only the structure of aiView and turn nodes into DOM yourself.
Demos
All three demos already display AI replies and can serve as references:
| Demo | Location |
|---|---|
| H5 demo | demo/src/features/ai.ts (plain DOM) |
| App demo | demo-app/components/ai-reply.vue |
| Mini program demo | demo-miniprogram/components/ai-reply.vue |
