Skip to content

v-ai directive

The v-ai directive can help you setup data-ai-id and data-ai-description. You can learn the usage of these 2 attributes at bai page.

The v-ai provide almost same functionality with bai, but v-ai directive automatically collect the DOM node and Vnode, you don't need to call .collect().

Setup

To use v-ai, you need setup vaiPlugin.

ts
// ...
import { vaiPlugin, VaiPluginOptions } from "@browser-ai/vai"

// ...
app.use<VaiPluginOptions[]>(vaiPlugin)

Usage

Usa v-ai directive in template

vue
<template>
  <h1 v-ai="{ id: 'title' }">Chatbot playground</h1>
  <p v-ai="{ id: 'paragraph' }">This is a page can play with ai chatbot. You can play with it by the buttons below</p>

  <div 
    v-ai="{
      id: 'actions',
      description: 'Buttons for interact with ai'
    }"
  >
    <!-- a lots of buttons.... -->
</div>
</template>

Then we can ask agent to pick element.

vue
<script setup lang="ts">
import { useCreateVai } from "@browser-ai/vai"
import { openaiClient } from "@/api/openai"

const createVai = useCreateVai()
const vai = createVai(openaiClient)

onMounted(() => {
  vai.withContext(`User: I want to play with ai !`, async () => {
    const element = await vai.whichElement("can fulfill user's purpose")
    // { id, description, el, vnode }
  
    element.el.scrollIntoView()

  })
})

</script>

TIP

You can also get vnode by element.vnode

WARNING

Keep in mind if you want do DOM manipulation, you have to do it after onMounted

Combine usage with .whichRoute()

It may be common using v-ai with .whichRoute for auto navigation. Check the combine usage with .whichElement().

There is a case you may encounter: after router navigation resolved, the element haven't be rendered, because they are waiting API response...So the agent can't see the elements before API respond. This make agent navigate user to wrong element.

Vai provide a tool routerWaiter to handle this situation.

For example, the following code:

ts
const navigation = async () => {
  vai.withContext(`User: do you have any product ?`, async () => {
    const route = await vai.whichRoute("can fulfill user's desire")
  if (!route){
    return
  }
  await router.push({ name: route.data.name })
  // navigation resolved

  // But the helpful elements may not exist
  const element = await vai.whichElement("can fulfill user's desire")
  if (!element){
    return
  }
  element.ele.scrollIntoView()
  // ...
  })
}

If the DOM node includes product info need wait api response, it is very possible the element haven't be rendered.

So we add routerWaiter:

ts
// PageIndex
import { useRouterWaiter } from "@browser-ai/vai"

const { pendingPush } = useRouterWaiter()

const navigation = async () => {
  // ...

  const route = await vai.whichRoute("can fulfill user's desire")
  // ...
  await pendingPush({ name: route.id })
  
  // ...
}

pendingPush() works like router.push(), but provide extra functionality to pending its promise.

We can call the pendingNavigation at the target page, like product page here:

vue
// PageProduct
<script setup>
import { useRouterWaiter } from "@browser-ai/vai"

const { pendingNavigation } = useRouterWaiter()

// this line will setup pending
const { release } = pendingNavigation()

onMounted(async () => {
  await fetchProductsAndSetup()
  
  // release the pendingPush promise.
  release()
})
</script>

When pendingNavigation is called, the pendingPush will wait util release is called.

We can ensure products exist in the page now, so we can ask vai pick a element.

ts
// PageIndex
import { useRouterWaiter } from "@browser-ai/vai"

const { pendingPush } = useRouterWaiter()

const navigation = async () => {
  // ...

  const route = await vai.whichRoute("can fulfill user's desire")
  // ...
  await pendingPush({ name: route.data.name })
  
  // After release
  const element = await vai.whichElement("can fulfill user's desire")
  if (!element){
    return
  }
  element.ele.scrollIntoView()
}

Route may has validation. Like a page need user login first, or some permission is needed. Validation can occur in many places, like beforeEach, beforeEnter......

ts
{
  path: "/",
  component: () => import("@/pages/MemberCenter.vue"),
  name: "MemberCenter",
  meta: {
    ai: {
      title: "MemberCenter",
      description:
        "The page for member only. Member can check their order info, personal info and manage their credit cards.",
    },
  },
  beforeEnter(){
    // only member can enter
    return false
  }
}

For these kind of cases, you can handle it by checking router.push() or pendingPush() returned results describe in vue-router doc.

ts
// pendingPush directly forwarding `router.push()` result
const navigationFailure = await pendingPush({ name: route.data.name })

if (navigationFailure) {
  // handle navigationFailure
}

// continue...