跳转到主要内容

Documentation Index

Fetch the complete documentation index at: https://docs.kontext.so/llms.txt

Use this file to discover all available pages before exploring further.

处理 no-fill

订阅 AdEvent.NoFill 即可知道何时没有广告返回(地域限制、频次封顶等)。skipCode 字段说明了原因。
onEvent = { event ->
    if (event is AdEvent.NoFill) {
        android.util.Log.d("kontext", "no fill: ${event.skipCode}")
    }
}

生命周期管理

对话结束或页面关闭时,请始终调用 session.destroy()(或其别名 session.close())。它会取消正在进行的预加载、销毁所有挂载的广告、结束 OMID 会话并释放 WebView 资源。幂等。 在 Compose 中,挂在页面级的 DisposableEffect 上:
DisposableEffect(session) {
    onDispose { session.close() }
}
在 ViewModel 中:
override fun onCleared() {
    session.destroy()
    super.onCleared()
}

动态更新会话选项

会话选项的一部分字段可以在不重建 session 的情况下动态更新。更新在下一次 /preload 时被读取,因此在下一条用户消息后生效。
import so.kontext.ads.model.MutablePublisherOptions

session.updateOptions(MutablePublisherOptions(variantId = "new-variant"))
可动态更新的字段:variantIdregulatoryuserEmailadvertisingId。非 null 字段覆盖原值;null 字段保持不变。如果要清空某个字段,请重建会话。
publisherTokenuserIdconversationIdenabledPlacementCodescharacter 不可 动态更新。中途修改会导致 /init 注册与后续请求脱节,或让累积的消息历史指向错误的角色。这些场景请重建会话。

中途更新同意状态

当用户在你的 CMP 中更新了同意状态,调用:
import so.kontext.ads.model.Regulatory

session.updateOptions(MutablePublisherOptions(
    regulatory = Regulatory(gdpr = 1, gdprConsent = "<new-TCF-string>"),
))
下一次预加载会带上新值——无需重建 session。

切换 character

character 不能动态更新——已累积的消息历史属于原本的角色;中途切换会导致后续消息以错误的人物画像被定向。 切换角色请销毁当前会话并新建:
session.destroy()

session = KontextAds.createSession(
    context = applicationContext,
    options = SessionOptions(
        publisherToken = "<your-publisher-token>",
        userId = "user-1234",
        conversationId = "conv-new",
        character = newCharacter,
    ),
)
publisherTokenuserIdconversationIdenabledPlacementCodes 同理——任何一个变化都请重建会话。

载入历史消息(会话恢复)

从你的后端恢复会话时,按顺序对每条历史消息调用一次 addMessage(...)。预加载本身带有防抖,因此连续的 add 调用会合并为对最近一条用户消息的单次预加载——不会因为恢复了 N 条消息就发出 N 次预加载。
for (historical in loadedFromBackend) {
    session.addMessage(
        Message(
            id = historical.id,
            role = historical.role,
            content = historical.content,
        ),
    )
}

把每条消息都喂入,用 trackOnly 控制是否展示广告

无论是否打算展示广告(付费用户、免广告地区、或每 N 条消息才展示一次广告等),都请把消息全部喂入会话。跳过 addMessage(...) 调用会破坏服务端用于定向的对话上下文。完整模式见 Pacing
import so.kontext.ads.model.AddMessageOptions

val shouldShowAd = userMessageCount % 5 == 0

session.addMessage(
    Message(id = msg.id, role = Role.USER, content = msg.content),
    AddMessageOptions(trackOnly = !shouldShowAd),
)
trackOnly = true 时,预加载仍会发送(服务端保留完整分析数据),但不会为该条消息触发 AdEvent.Filledsession.createAd(...) 也不会拿到可填充的广告。

主题

InlineAd composable 传入一个发布方自定义的主题字符串;它会进入 iframe 的 update-iframe 负载,供广告创意读取。
InlineAd(
    messageId = msg.id,
    session = session,
    theme = if (isSystemInDarkTheme()) "dark" else "light",
)
主题是 Ad 身份键的一部分——同一个 messageId 下切换主题会让 iframe 以新的主题重新加载。

View 互操作(非 Compose)

非 Compose 项目请使用 InlineAdViewFrameLayout 子类)。像普通 view 一样实例化或 inflate,然后调用 bind(messageId, session)
import so.kontext.ads.ui.InlineAdView

val adView = InlineAdView(context)
adView.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
adView.onHeightChange = { newHeight ->
    // 如果你在 RecyclerView 行里,请重新触发行布局
    requestRowLayout()
}
adView.bind(messageId = assistantMessageId, session = session)
container.addView(adView)
InlineAdView 遵循与 composable 相同的生命周期规则——页面销毁时调用 session.destroy();view 内的 WebView 由会话的池来回收。