🧩 TavoJS API

Since v0.75.0

📖 概述

TavoJS API 是面向玩家及创作者提供的一套 JavaScript 接口,以方便用户在开启 JavaScript 支持时可以获得强大的功能和高可玩性。

插件生命周期边界: generation:preparegeneration:successgeneration:errorgeneration:cancelled 不是纯 TavoJS 或 tavo.events API。 只有已安装插件的 entry 脚本能通过 tavo.plugin.on(...) 注册;HTML 片段脚本不能注册。请参阅 插件开发

✨ 氛围编程 (Vibe Coding) 友好

对于非专业编程玩家,我们建议将此文档复制给 AI ,然后让 AI 生成与 Tavo 结合紧密的高可玩性代码!

⚙️ 变量

变量用于存储数据,JavaScript 本身的变量只能存活于页面中,一旦刷新就会丢失,因此我们提供一组变量 API 来协助用户长期存储数据。

获取变量

tavo.get(<name>[, <scope>])

例如:

let age = tavo.get('age')  // 获取聊天变量中的 age
let bestScore = tavo.get('bestScore', 'global')  // 获取全局最高分
let hp = tavo.get('hp', 'message')  // 获取当前消息(楼层)上的 hp

作用域 (scope)

作用域是变量可用范围,我们支持以下几种:

  • chat: 聊天作用域,这是默认的作用域。变量仅在当前聊天内可访问,你应该总是优先考虑此作用域(可随聊天导出)
  • global: 全局作用域。变量将可以跨对话访问与保存(因此也需要格外小心命名冲突)
  • message: 消息作用域。变量挂在单条消息(楼层)上,随消息删除一并消失(详见下方「消息作用域」一节)

⚠️ 不同作用域中的变量完全不通,不存在跨作用域覆盖。

设置变量

tavo.set(<name>, <value>[, <scope>])

例如:

tavo.set('age', 16)  // 设置聊天变量 age = 16
tavo.set('Lily_lover', 'Colin', 'global')  // 设置全局变量,Lily 的爱人为 Colin
tavo.set('status', { hp: 100, mp: 32, location: 'Cave' })  // 设置当前聊天的状态为:生命值 100,魔法值 32,地点 洞穴

更新变量

tavo.update(<name>, <value>[, <scope>])

tavo.set(...) 最大的不同,是允许对 object 值进行部分更新,例如:

tavo.set('status', { hp: 100, mp: 32 })  // status = { hp: 100, mp: 32 }
tavo.update('status', { hp: 70 })  // status = { hp: 70, mp: 32 }
tavo.update('status', { status: 'poisoned' })  // status = { hp: 70, mp: 20, status: 'poisoned' }

删除变量

tavo.unset(<name>[, <scope>])

例如:

tavo.set('age', 16)  // age = 16
tavo.unset('age')  // age = null

变量路径

当操作变量时,我们支持路径形式,例如:

tavo.set('status', { hp: 100, mp: 50 })
tavo.get('status.hp')  // 100
tavo.unset('status.hp')  // status = { mp: 50 }

消息作用域

Since v0.88.0

除了 chat / global,变量还能挂到**单条消息(楼层)**上,随该消息删除一并消失。适合把状态绑定到具体某条回复——比如该楼角色的心情、HP、回合数。

scope 传字符串 'message',指当前宿主楼层(执行此代码所在的那条消息;不在气泡环境中执行时按最后一层处理)。也可以传对象 { scope: 'message', id: n },按消息 id(稳定主键,删楼不漂移)写到指定的某条消息。

tavo.set('hp', 100, 'message')                       // 写到当前楼层(宿主语义)
let hp = tavo.get('hp', 'message')                   // 读当前楼层的 hp
tavo.set('hp', 50, { scope: 'message', id: 2338 })   // 按消息 id 写到指定楼层
tavo.unset('hp', 'message')                          // 删当前楼层的 hp

在提示词中使用变量

可以通过提示词将变量发给模型,只需要在提示词中使用 宏 (macros) 即可:

{{getvar::<name>}} 获取变量(作用域为 chat,当前聊天) {{getglobalvar::<name>}} 获取变量(作用域为 global,全局)

例如:

{{char}} 有了一个新名字 {{getvar::name}}
{{user}} 当前生命值 {{getvar::status.hp}}
全局历史最高分 {{getglobalvar::highestScore}}

更多的变量宏请参考 related page

💬 消息

Since v0.78.0

可以通过此接口读取或改变消息,所有消息接口均为 tavo.message.<method>(...)

查找消息

await tavo.message.find(<indexRange>[, <filter>])

按照楼层范围 indexRange 和过滤器 filter 查找消息,返回值为数组,其中:

indexRange 类型 number | array: 楼层范围

  • 当为 number 类型时:
    1. 获取指定楼层消息
    2. 楼层从 0 开始,第一条消息为 0,第二条消息为 1……以此类推
    3. 支持负数从尾部开始计算楼层, -1 为最后一条,-2 为倒数第二条……以此类推
  • 当为 array 类型时:
    1. [start, end] 例如 [2, 4],会取出第 2、3、4 条记录(双侧闭区间)
    2. [start] 代表从 start 开始到最后
    3. [0, end] 代表从第 0 条开始到 end
    4. [] | null | undefined 代表全部楼层
  • 无论何种参数,总是返回数组,若指定楼层不存在,返回空数组 []

filter 类型 object: 过滤条件

  • role 类型 string 按角色过滤,可选值(默认为所有):
    1. 'system' 系统消息
    2. 'assistant' 角色消息
    3. 'user' 用户消息
  • hidden 类型 boolean 是否包含隐藏消息,可选值(默认为所有):
    1. true 仅包含隐藏消息
    2. false 仅包含非隐藏消息
  • characters 类型 array 角色 ID 数组,仅过滤传入的角色发出的消息

消息格式为:

{
  id: 2338,  // 消息 ID
  characterId: 34,  // 角色 ID(仅 assistant 消息会有)
  content: 'Hello!',  // 消息内容
  hidden: false,  // 是否是隐藏消息
  role: 'assistant'  // 消息角色
}

例如:

await tavo.message.find(2)  // 获取第 3 层的消息
await tavo.message.find([3, 100])  // 获取第 3-100 条消息,若总共只有 50 层,则返回 3-50 层
await tavo.message.find(-1, { role: 'user' })  // 用户发的最后一层消息
await tavo.message.find([10], { hidden: false })  // 未隐藏的消息,楼层 >= 10 的所有消息

获取单条消息

await tavo.message.get(<messageId>)

按消息 ID 获取单条消息,若 ID 无效或消息不存在则返回 null

let msg = await tavo.message.get(2338)  // 获取 ID 为 2338 的消息

获取当前消息

await tavo.message.current()

获取执行此代码所在的那条消息对象,字段与上文「消息格式」及 tavo.message.get 一致。

典型用途:读取本条消息上的角色信息(tavo.character.get(currentMessage.characterId)),或是修改本条消息(调用 tavo.message.update 写回)。

const self = await tavo.message.current()
console.log(self)

获取消息总数

await tavo.message.count()

获取当前聊天中的消息总数(包含隐藏消息)。一般用来定位最后一条的楼层,第一条楼层为 0,最后一条楼层为 消息总数 - 1

let lastIndex = await tavo.message.count() - 1
console.log(lastIndex)

追加消息

await tavo.message.append(<message>)

在当前聊天末尾追加一条消息,成功返回新消息 ID,失败返回 null

message 类型 object,常见字段:

  • content 类型 string:消息内容(必填)
  • role 类型 string'assistant' | 'user'(默认按 'assistant' 处理),角色消息还是用户消息
  • characterId 类型 number:当 role = 'assistant' 时可指定发言角色 ID(单聊中可不传,群聊必传)
  • hidden 类型 boolean:是否为隐藏消息(默认 false

注意:

  1. role = 'assistant' 且未传 characterId 时,会按当前会话上下文自动推断角色
  2. 若无法推断角色,或角色不属于当前聊天,会创建失败并返回 null

例如:

let newId = await tavo.message.append({
  role: 'assistant',
  characterId: 34,
  content: '这是追加的一条消息',
  hidden: false,
})

单聊中创建非隐藏消息时,可简化为:

let newId = await tavo.message.append({
  content: '这是追加的一条消息。role 默认为 assistant ,即角色消息;单聊时自动推断角色;hidden 默认为 false',
})

更新消息

await tavo.message.update(<message>, <opts?>)

按消息 ID 更新一条已有消息,成功返回消息 ID,失败返回 null

message 类型 object,常见字段:

  • id 类型 number:要更新的消息 ID(必填)
  • content 类型 string:更新后的消息内容(必填)
  • reasoning 类型 string:推理内容(可选,传空字符串会清空)
  • hidden 类型 boolean:是否隐藏(可选,默认按 false 处理)

opts 类型 object,可选字段:

  • reuseContext 类型 boolean:是否保留当前气泡的脚本执行环境。默认 false
const lastMessage = (await tavo.message.find(-1))[0]  // 获得最后一层的消息 (参见 tavo.message.find 说明)
lastMessage.content = '更新后的内容'
lastMessage.reasoning = '可选推理内容'
lastMessage.hidden = true  // 改为隐藏消息
await tavo.message.update(lastMessage)  // 更新最后一条消息

reuseContext 说明

如果你的脚本调用 tavo.message.update 更新自己所在的气泡,并且希望更新后继续执行后续脚本,传 reuseContext: true

// 脚本里调用,且更新的是自己所在的气泡
await tavo.message.update(self, { reuseContext: true })
console.log('更新后继续执行')

删除消息

await tavo.message.delete(<messageId>)

按消息 ID 删除消息,成功返回被删除的消息 ID,失败返回 null

const count = await tavo.message.count();  // 获得总消息数
const midIndex = Math.floor(count / 2);
const midMessage = (await tavo.message.find(midIndex))[0]  // 获得中间一条消息
await tavo.message.delete(midMessage.id)  // 删除中间那条消息

🗨️ 聊天

可以通过此接口获取当前聊天信息,所有聊天接口均为 tavo.chat.<method>(...)

获取当前聊天

await tavo.chat.current()

获取当前正在进行的聊天信息,若当前没有聊天则返回 null

例如:

let chat = await tavo.chat.current()
console.log(chat.name)        // 打印当前聊天名称
console.log(chat.characters[0]?.name)  // 打印第一个角色名称
console.log(chat.persona?.name)        // 打印当前用户身份名称(若有)

异步 await/async TavoJS API 中,除变量操作外,几乎所有的 API 接口都需要使用异步调用。 异步调用就是调用时在前面写上 await,例如 let chat = await tavo.chat.current(),如果你忘了 await 写成了 let chat = tavo.chat.current(),就会出错(可以在侧边栏的 JavaScript 控制台中查看日志)。 而 await 只能在 async 函数(或模块顶层)中使用,比如:

async function demo() {
  let chat = await tavo.chat.current();
}

同样如果忘了在 function 前写 async 但函数内部又调用了 await 也会报错噢!

简言之:除变量操作外,所有 TavoJS API 调用前面必须加 await,而调用的函数要用 async 声明。

更新当前聊天

await tavo.chat.update(<chat>)

更新当前聊天。

可更新字段:

  • name:聊天标题
  • characters:角色 ID 数组(会直接替换当前聊天角色列表)
  • persona:用户身份 ID
  • background:聊天背景(会话级覆盖,不影响主题;详见下方)
await tavo.chat.update({
  name: '新的聊天标题',
  characters: [12, 34],
  persona: 5,
})

注意:该接口仅更新“当前聊天”,不支持按聊天 ID 更新其他会话。

设置聊天背景 background

会话级背景覆盖独立于聊天主题,不会改动或创建主题。三种来源互斥,优先级为 useAvatar > image > color

// 使用图片背景(URL,或 tavo.file.save 返回的相对路径,如 'files/chat/bg.png')
await tavo.chat.update({ background: { image: 'files/chat/bg.png', opacity: 0.85 } })

// 使用当前角色头像作为背景
await tavo.chat.update({ background: { useAvatar: true } })

// 使用纯色背景(hex)
await tavo.chat.update({ background: { color: '#222222' } })

// 清除会话级覆盖,回落到主题背景
await tavo.chat.update({ background: null })
  • image 类型 string:图片背景。支持 http(s):// 网图,或相对当前文档目录的本地路径(推荐配合 tavo.image.generate + tavo.file.save 落盘后传入)。不要传绝对路径。
  • useAvatar 类型 boolean:为 true 时使用当前会话角色头像作为背景。
  • color 类型 string'#RRGGBB''#AARRGGBB' 纯色背景。
  • opacity 类型 number(0-1):图片不透明度,仅图片背景有意义。
  • background: null 清除覆盖;不传 background 键则不改动。

聊天对象字段

聊天对象(current 返回)包含以下常见字段:

{
  id: 1,                    // 聊天 ID
  name: '与爱丽丝的对话',    // 聊天名称
  characters: [             // 聊天中的角色概要列表
    {
      id: 12,
      name: 'Alice',
      avatar: 'alice.png'
    },
    {
      id: 7,
      name: 'Lee',
      avatar: 'lee.png'
    },
  ],
  persona: {                // 当前使用的用户身份概要(可能为 null)
    id: 5,
    name: '默认用户身份',
  },
  preset: {                 // 当前使用的预设概要
    id: 9,
    name: '默认预设',
  },
  lorebooks: [{
    id: 17,
    name: '不夜城',
  }],
  regexes: [{               // 当前启用的正则概要列表
    id: 3,
    name: '移除舞台提示',
  }],
  background: {             // 会话级背景覆盖;无覆盖时为 null
    image: 'files/chat/bg.png',
    opacity: 0.85,
  },
}

🧙 角色

可以通过此接口管理角色,所有角色接口均为 tavo.character.<method>(...)

获取所有角色概要

await tavo.character.all()

返回角色概要对象数组(每项仅包含 idnameavatar 等概要信息):

let chars = await tavo.character.all()
console.log(chars[0].id)     // 例如 12
console.log(chars[0].avatar) // 例如 "chara/alice.png"
console.log(chars[0].name)   // 例如 "Alice"

获取单个角色

await tavo.character.get(<characterId>)

按角色 ID 获取角色对象,不存在时返回 null

let char = await tavo.character.get(12)
if (char) {
  console.log(char.name)
}

按名称查找角色

await tavo.character.find(<name>[, <options>])

按名称查找角色,返回角色对象数组。options.match 可选:'exact' | 'prefix' | 'suffix' | 'contains'(默认 'exact'

let chars = await tavo.character.find('Alice')
let chars2 = await tavo.character.find('Ali', { match: 'prefix' })
console.log(chars.length)

新建角色

await tavo.character.create(<character>)

创建角色并返回新角色 ID。character.namecharacter.firstMes 为必填项。

CCv3 兼容: 创建和更新时同样接受 CCv3 规范的字段名(如 first_mesmes_examplecreator_notes 等),会自动转换为 tavo 格式。

let id = await tavo.character.create({
  name: 'Alice',
  firstMes: '你好,我是 Alice。',
  description: '一位温柔的向导',
})

更新角色

await tavo.character.update(<character>)

更新角色并返回角色 ID。character.idcharacter.namecharacter.firstMes 为必填项。

await tavo.character.update({
  id: 12,
  name: 'Alice',
  firstMes: '你好,我是 Alice。',
  personality: '耐心、细致',
})

导入角色卡

await tavo.character.import(<card>)

导入一个完整的 CCv3 角色卡对象(\{ spec: "chara_card_v3", data: \} 格式或裸 data 对象均可)。若卡片包含 character_book,会同时创建世界书;包含 extensions.regex_scripts,会同时创建正则脚本。操作前会弹窗请求用户确认。

返回值:

{
  characterId: 12,     // 创建的角色 ID
  lorebookId: 5,       // 创建的世界书 ID(若无则为 null)
  regexId: 3,          // 创建的正则脚本 ID(若无则为 null)
}
const result = await tavo.character.import(card)
// result.characterId, result.lorebookId, result.regexId

删除角色

await tavo.character.delete(<characterId>)

按角色 ID 删除角色:

await tavo.character.delete(12)
await tavo.character.delete(char)  // char 需要时带 id 的角色对象

角色对象字段

角色对象(get / find 返回)包含以下常见字段:

{
  id: 12,                    // 角色的唯一 ID
  avatar: 'xxx.png',         // 角色头像图片 URL 或路径
  name: 'Alice',             // 角色名称(必填)
  description: '...',        // 角色简介/描述
  firstMes: '...',           // 角色打招呼内容(必填)
  personality: '...',        // 角色性格描述
  scenario: '...',           // 适用场景或使用场景描述
  mesExample: '...',         // 消息示例,以 <START> 分割
  creatorNotes: '...',       // 创建者注释或补充说明
  systemPrompt: '...',       // 系统提示词
  postHistoryInstructions: '...',  // 信息上下文历史后的额外提示或说明
  alternateGreetings: ['...'],     // 角色可用的备用打招呼
  tags: ['guide'],           // 角色标签,用于分类或检索
  creator: 'Colin',          // 创建者用户名或昵称
  characterVersion: '1.0',  // 角色版本号
  nickname: 'Ali',           // 角色昵称或别名,如果填写了将替代 name 作为 {{char}} 的输出
  groupOnlyGreetings: ['...'],     // 仅限群聊使用的特定打招呼语
  creationDate: new Date('2026-03-05T10:20:30.000Z'),      // 创建时间(Date 对象)
  modificationDate: new Date('2026-03-05T11:30:00.000Z'),  // 最后修改时间(Date 对象)
}

说明:创建、更新、导入、删除角色时会弹出确认框,用户取消后操作不会生效。

🎭 用户身份

可以通过此接口管理用户身份,所有用户身份接口均为 tavo.persona.<method>(...)

获取所有用户身份概要

await tavo.persona.all()

返回用户身份概要对象数组(每项包含 idname):

let personas = await tavo.persona.all()
console.log(personas[0].id)    // 例如 5
console.log(personas[0].name)  // 例如 "默认用户身份"

获取单个用户身份

await tavo.persona.get(<personaId>)

按用户身份 ID 获取用户身份对象,不存在时返回 null

let persona = await tavo.persona.get(5)
if (persona) {
  console.log(persona.name)
  console.log(persona.description)
}

按名称查找用户身份

await tavo.persona.find(<name>[, <options>])

按名称查找用户身份,返回用户身份对象数组。options.match 可选:'exact' | 'prefix' | 'suffix' | 'contains' (默认 'exact'

let personas = await tavo.persona.find('默认')
let personas2 = await tavo.persona.find('默', { match: 'prefix' })
console.log(personas.length)

新建用户身份

await tavo.persona.create(<persona>)

创建用户身份并返回新用户身份 ID。persona.namepersona.description 为必填项。

let id = await tavo.persona.create({
  name: '侦探用户身份',
  description: '注重细节,擅长结构化推理。',
  avatar: 'chara/persona-detective.png',
})

更新用户身份

await tavo.persona.update(<persona>)

更新用户身份。persona.idpersona.namepersona.description 为必填项。

await tavo.persona.update({
  id: 5,
  name: '默认用户身份',
  description: '语气更简洁,优先给出可执行结论。',
  avatar: 'chara/persona-default.png',
  active: true,
})

删除用户身份

await tavo.persona.delete(<personaId>)

按用户身份 ID 删除用户身份:

await tavo.persona.delete(5)
await tavo.persona.delete(persona)  // persona 需要是带 id 的用户身份对象

用户身份对象字段

用户身份对象(get 返回)包含以下常见字段:

{
  id: 5,  // 用户身份唯一 ID
  name: '默认用户身份',  // 用户身份名称(必填)
  description: '...',  // 用户身份描述(必填)
  avatar: 'xxx.png',  // 用户身份头像 URL 或路径(可选)
  active: true,  // 是否为默认用户身份
  sortIndex: 12,  // 排序索引
}

🎛️ 预设

可以通过此接口管理预设,所有预设接口均为 tavo.preset.<method>(...)

获取所有预设(摘要)

await tavo.preset.all()

返回预设摘要对象数组(每项包含 idname):

let presets = await tavo.preset.all()
console.log(presets[0].id)    // 例如 1
console.log(presets[0].name)  // 例如 "Default"

获取单个预设

await tavo.preset.get(<presetId>)

按预设 ID 获取完整预设对象,不存在时返回 null

let preset = await tavo.preset.get(1)
if (preset) {
  console.log(preset.name)
  console.log(preset.entries.length)
  console.log(preset.basicPrompts.chatStart)
}

按名称查找预设

await tavo.preset.find(<name>[, <options>])

按名称查找预设,返回完整预设对象数组。options.match 可选:'exact' | 'prefix' | 'suffix' | 'contains' (默认 'exact'

let presets = await tavo.preset.find('Default')
let presets2 = await tavo.preset.find('Def', { match: 'prefix' })
console.log(presets.length)

导入预设

await tavo.preset.import(<preset>)

以 SillyTavern 预设格式导入预设,弹出确认对话框,用户确认后保存并返回新预设 ID,取消则返回 nullpreset.name 为预设名称(可选,缺省为 'Preset')。

const id = await tavo.preset.import({
  name: 'My Preset',
  prompts: [...],
  prompt_order: [{ character_id: 100001, order: [...] }],
})

新建预设

await tavo.preset.create(<preset>)

创建预设并返回新预设 ID。preset.name 为必填项,其余字段可选;preset.basicPromptspreset.entries 中缺失的部分将自动填充默认值。

let id = await tavo.preset.create({
  name: '我的预设',
  basicPrompts: {
    continueNudge: '[继续你的上一条消息,不要重复原有内容。]',
  },
  entries: [
    {
      identifier: 'abc123',
      name: '🌸 文风控制',
      content: '采用精致优雅的叙事风格,类似晋江、长佩等平台受欢迎的高质量女性向作品。',
    },
  ],
})

更新预设

await tavo.preset.update(<preset>)

更新预设。preset.id 为必填项。传入的 entries 会直接覆盖原有的 entries,典型用法是先 get 取出,修改后再 update 写回。

const preset = await tavo.preset.get(33);
preset.entries.find(e => e.identifier == 'main').content = '请用中文回复 {{user}} 的所有问题。';
await tavo.preset.update(preset)

删除预设

await tavo.preset.delete(<presetId>)

按预设 ID 删除预设:

await tavo.preset.delete(1)
await tavo.preset.delete(preset)  // preset 需要是带 id 的预设对象

预设对象字段

完整预设对象(get / find 返回)包含以下字段:

{
  id: 1,          // 预设唯一 ID
  name: 'Default', // 预设名称(必填)
  basicPrompts: { /* BasicPrompts,见下 */ },
  entries: [],    // PresetEntry[] 提词条目列表(见下)
}

基础提词字段(BasicPrompts)

basicPrompts 包含各类系统提词模板,所有字段均可选,缺省时使用内置默认值:

{
  persona: '{{persona}}',        // 用户身份描述的格式模板
  description: '{{description}}', // 角色描述的格式模板
  personality: '{{personality}}', // 角色性格的格式模板(用 {{personality}} 标记插入位置)
  scenario: '{{scenario}}',      // 场景的格式模板(用 {{scenario}} 标记插入位置)
  exampleMessageStart: '[Example Chat]',  // 示例对话起始标记
  chatStart: '[Start a new Chat]',        // 聊天历史起始标记
  groupChatStart: '[Start a new group chat. Group members: {{group}}]',  // 群聊起始标记
  groupNudge: '[Write the next reply only as {{char}}.]',  // 群聊中催促指定角色回复的提词
  continueNudge: '[Continue your last message without repeating its original content.]',  // 续写按钮的提词
  impersonation: '[Write your next reply from the point of view of {{user}}...]',  // 扮演用户时的提词
  lorebook: '{0}',  // 世界书条目的包装模板(用 {0} 标记内容插入位置)
}

提词条目字段(PresetEntry)

entries 数组中每一项的结构:

{
  // ── 基本信息 ──────────────────────────────────
  identifier: 'main',   // 条目唯一标识(内置条目有固定 identifier,见下表)
  name: 'Main Prompt',  // 条目显示名称
  content: '...',       // 提词正文(marker 类型无此字段)
  enabled: true,        // 是否启用此条目(在激活列表中是否生效)
  active: true,         // 是否加入激活列表(false 时条目仅存档,不参与提示词构建)

  // ── 类型 ──────────────────────────────────────
  type: 'custom',       // 条目类型:
                        //   'builtin' - 内置提词(固定 identifier,如 main / jailbreak)
                        //   'marker'  - 位置标记(无内容,仅标记其他内容的插入位置)
                        //   'custom'  - 自定义提词

  // ── 角色与注入(custom 类型可配置)──────────────
  role: 'system',       // 消息角色:'system' | 'user' | 'assistant'
  injectionPosition: 'relative',  // 注入位置:
                                  //   'relative' - 相对位置(跟随预设列表顺序)
                                  //   'absolute' - 绝对位置(插入到聊天历史的特定深度)
  injectionDepth: 4,   // 注入深度,仅 injectionPosition 为 'absolute' 时生效
                        // 0 = 最后一条消息之后,1 = 最后一条消息之前,以此类推
}

内置条目 identifier 列表

以下 identifier 对应系统内置的固定提词或位置标记,创建 / 更新时可直接引用:

identifier名称类型说明
mainMain Promptbuiltin主提词,对话的核心指令
worldInfoBeforeLorebook Beforemarker世界书(角色描述上方)插入点
personaDescriptionPersona Descriptionmarker用户身份描述插入点
charDescriptionChar Descriptionmarker角色描述插入点
charPersonalityChar Personalitymarker角色性格插入点
scenarioScenariomarker场景描述插入点
enhanceDefinitionsEnhance Definitionsbuiltin增强角色定义的补充提词
nsfwAuxiliary Promptbuiltin辅助提词(默认为空)
worldInfoAfterLorebook Aftermarker世界书(角色描述下方)插入点
dialogueExamplesChat Examplesmarker示例对话插入点
chatHistoryChat Historymarker聊天历史插入点
jailbreakPost-History Instructionsbuiltin历史记录后的补充指令

📚 世界书

可以通过此接口管理世界书,所有世界书接口均为 tavo.lorebook.<method>(...)

获取所有世界书概要

await tavo.lorebook.all()

返回世界书概要对象数组(每项包含 idnameentries):

let lorebooks = await tavo.lorebook.all()
console.log(lorebooks[0].id)       // 例如 3
console.log(lorebooks[0].name)     // 例如 "城市设定"
console.log(lorebooks[0].entries)  // 例如 12(条目数量)

获取单个世界书

await tavo.lorebook.get(<lorebookId>)

按世界书 ID 获取对象,不存在时返回 null

let lorebook = await tavo.lorebook.get(3)
if (lorebook) {
  console.log(lorebook.name)
  console.log(lorebook.entries.length)
}

按名称查找世界书

await tavo.lorebook.find(<name>[, <options>])

按名称查找世界书,返回世界书对象数组。options.match 可选:'exact' | 'prefix' | 'suffix' | 'contains' (默认 'exact'

let lorebooks = await tavo.lorebook.find('城市')
let lorebooks2 = await tavo.lorebook.find('城市', { match: 'suffix' })
console.log(lorebooks.length)

导入世界书

await tavo.lorebook.import(<lorebook>)

导入一个 CCv3 character_book 格式的世界书对象(含 entries 数组)。操作前会弹窗请求用户确认。返回新世界书 ID,取消则返回 null

const id = await tavo.lorebook.import({
  name: 'My Lorebook',
  entries: [...]
})

新建世界书

await tavo.lorebook.create(<lorebook>)

创建世界书并返回新世界书 ID。lorebook.name 为必填项。

let id = await tavo.lorebook.create({
  name: '城市设定',
  entries: [],
})

更新世界书

await tavo.lorebook.update(<lorebook>)

更新世界书。lorebook.idlorebook.name 为必填项。

await tavo.lorebook.update({
  id: 3,
  name: '城市设定(重制)',
  entries: [],
})

删除世界书

await tavo.lorebook.delete(<lorebookId>)

按世界书 ID 删除世界书:

await tavo.lorebook.delete(3)
await tavo.lorebook.delete(lorebook)  // lorebook 需要是带 id 的世界书对象

世界书对象字段

世界书对象(get / find 返回)包含以下字段:

{
  id: 3,           // 世界书唯一 ID
  name: '城市设定', // 世界书名称(必填)
  entries: [],     // LorebookEntry[] 条目列表(见下)
}

条目对象字段(LorebookEntry)

entries 数组中每一项的结构:

CCv3 兼容: 创建和更新时同样接受 CCv3 规范的字段名,会自动转换。部分字段不只是改名,语义也有差异,映射规则如下:

  • keyskeywordssecondary_keyssecondaryKeywords
  • constant: true/falsestrategy: 'constant'/'keyword'
  • position: 'before_char'/'after_char'injectionPosition: 'lorebookBefore'/'lorebookAfter'
  • selective: true/falsesecondaryKeywordStrategy: 'andAny'/'none'
{
  // ── 基本信息 ──────────────────────────────────
  identifier: 'entry-uuid',  // 条目唯一标识(字符串)
  name: '城市总览',            // 条目名称(仅供显示和搜索)
  content: '这是一座临海城市,夜间常有浓雾。',  // 注入到提示词的正文内容
  enabled: true,             // 是否启用此条目
  strategy: 'constant',      // 触发策略:'constant'(常驻)| 'keyword'(关键词触发)

  // ── 关键词 ─────────────────────────────────────
  keywords: ['城市', '港口'],         // 主关键词列表(strategy 为 'keyword' 时生效)
  secondaryKeywords: ['夜晚', '雾'],  // 次级关键词列表
  secondaryKeywordStrategy: 'none',  // 次级关键词匹配策略:
                                     //   'none'   - 不启用次级关键词
                                     //   'andAny' - 主词命中且任意次级词命中(默认)
                                     //   'andAll' - 主词命中且全部次级词命中
                                     //   'notAny' - 主词命中且没有次级词命中
                                     //   'notAll' - 主词命中且不是全部次级词命中
  scanDepth: 2,              // 关键词扫描的消息深度(默认 2,最大 1000)
  caseSensitive: false,      // 关键词是否区分大小写
  matchWholeWord: true,      // 是否全词匹配

  // ── 注入位置 ───────────────────────────────────
  injectionPosition: 'lorebookBefore',  // 注入位置:
                                        //   'lorebookBefore'         - 角色描述上方(↑Char)
                                        //   'lorebookAfter'          - 角色描述下方(↓Char)
                                        //   'topOfExampleMessages'   - 示例对话之前
                                        //   'bottomOfExampleMessages'- 示例对话之后
                                        //   'atDepth'                - 聊天历史的绝对深度位置
  injectionDepth: 4,         // 注入深度,仅 injectionPosition 为 'atDepth' 时生效
  injectionRole: 'system',   // 注入角色:'system' | 'user' | 'assistant'

  // ── 概率与行为 ─────────────────────────────────
  probability: 100,  // 激活概率(0–100,默认 100)
  sticky: 0,         // 激活后持续保持的消息轮数(0 表示不持续)
  cooldown: 0,       // 激活一次后的冷却轮数(0 表示无冷却)
  delay: 0,          // 延迟激活的消息轮数(0 表示立即)
}

🎨 正则

可以通过此接口管理正则组(一组查找/替换规则),所有正则接口均为 tavo.regex.<method>(...)

获取所有正则(摘要)

await tavo.regex.all()

返回正则摘要对象数组(每项包含 idnameentries,其中 entries规则条数,不是条目数组):

let list = await tavo.regex.all()
console.log(list[0].id)       // 例如 2
console.log(list[0].name)     // 例如 "我的正则"
console.log(list[0].entries)  // 例如 5(条规则数量)

获取单个正则

await tavo.regex.get(<regexId>)

按 ID 获取完整正则对象,不存在时返回 null

let r = await tavo.regex.get(2)
if (r) {
  console.log(r.name)
  console.log(r.entries.length)
}

按名称查找正则

await tavo.regex.find(<name>[, <options>])

按名称查找正则,返回完整正则对象数组。options.match 可选:'exact' | 'prefix' | 'suffix' | 'contains' (默认 'exact'

let found = await tavo.regex.find('我的')
let found2 = await tavo.regex.find('我的', { match: 'contains' })
console.log(found.length)

导入正则

await tavo.regex.import(<regex>)

以 SillyTavern 正则格式导入正则组,弹出确认对话框,用户确认后保存并返回新正则组 ID,取消则返回 nullregex.name 为组名(可选,缺省为 'Regex');regex.entries 为 SillyTavern 格式的规则数组。

const id = await tavo.regex.import({
  name: '高亮处理',
  entries: [
    { scriptName: '高亮', findRegex: '\\[高亮:(.+?)\\]', replaceString: '<mark>$1</mark>', placement: [2], disabled: false, markdownOnly: true, promptOnly: false, runOnEdit: false, substituteRegex: 0 }
  ]
})

新建正则

await tavo.regex.create(<regex>)

创建正则并返回新 ID。regex.name 为必填;regex.entries 为规则数组,可省略(视为空列表)。创建 / 更新前会弹出确认对话框。

let id = await tavo.regex.create({
  name: 'Demo 正则',
  entries: [
    {
      name: '状态栏',
      findRegex: '/<status>(.*?)<\/status>/gim',
      replaceString: '<pre>$1</pre>',
      placements: ['char'],
      timing: 'display',
    },
  ],
})

更新正则

await tavo.regex.update(<regex>)

更新正则。regex.idregex.name 均为必填(前端封装会校验)。典型用法:get → 修改 → update

const r = await tavo.regex.get(2)
r.entries[0].enabled = false
await tavo.regex.update(r)

删除正则

await tavo.regex.delete(<regexId>)

按 ID 删除;也可传入带 id 的正则对象:

await tavo.regex.delete(2)
await tavo.regex.delete({ id: 2 })

正则对象字段

完整对象(get / find 返回)结构:

{
  id: 2,
  name: '我的正则',
  entries: [ /* RegexEntry[],见下 */ ],
}

规则条目字段(RegexEntry)

entries 中每一项:

{
  name: '规则显示名',             // 必填(字符串),否则解析可能失败
  findRegex: 'pattern',          // 查找用正则(可支持 JavaScript 正则类似的 `/pattern/flags` 写法)
  replaceString: '',             // 替换为的字符串
  trimStrings: [],               // 额外要裁剪的字符串列表
  placements: ['char'],          // 作用位置,可多选:
                                 //   'user'      - 用户输入
                                 //   'char'      - AI 输出
                                 //   'reasoning' - 推理内容
                                 //   'lorebook'  - 世界书注入内容
  timing: 'display',           // 执行时机:
                                 //   'display'         - 仅显示时(不写入持久消息,类似 ST markdownOnly)
                                 //   'send'            - 仅发送进模型前
                                 //   'sendAndDisplay'  - 显示与发送都执行
                                 //   'receive'         - 收到回复后持久化(仅输入/输出相关)
                                 //   'editAndReceive'  - 收到与编辑消息时都会持久化改写
  substitution: 'none',        // 宏替换方式:'none' | 'raw' | 'escaped'
  minDepth: null,              // 可选,消息深度下限(整数)
  maxDepth: null,              // 可选,消息深度上限(整数)
  enabled: true,               // 是否启用该条规则
}

省略字段时,端侧会为 findRegexreplaceStringtrimStringsplacementstimingsubstitutionenabled 等填入合理默认值(例如 placements: ['char']timing: 'display')。

🧠 长记忆

可以通过此接口读取或修改当前聊天的长期记忆(Long-term Memory),所有接口均为 tavo.memory.<method>(...)

获取当前记忆

await tavo.memory.current()

获取当前聊天记忆对象:

const memory = await tavo.memory.current()
console.log(memory.enabled)         // true / false
console.log(memory.memories.length) // 记忆条数

更新记忆

await tavo.memory.update(<memory>)

更新当前聊天记忆并返回更新后的对象。可更新字段:

  • enabled:是否启用记忆
  • memories:记忆文本数组(string[]
const memory = await tavo.memory.current()

memory.enabled = true
memory.memories = [
  '用户喜欢简洁、结论先行的回答风格',
  '用户倾向于让角色保持冷静和专业',
]

const updated = await tavo.memory.update(memory)
console.log(updated)

记忆对象字段

current / update 返回对象结构:

{
  id: 12,  // 记忆记录 ID
  enabled: true,  // 是否启用长期记忆
  memories: [     // 记忆条目列表(字符串数组)
    '用户偏好简洁回复',
    '避免重复解释已确认信息'
  ],
}

✨ 生成请求

可以通过此接口直接触发一次文本生成,所有生成接口均为 tavo.generate(...)

发起生成

await tavo.generate(<prompt>, <options>)

  • prompt 类型 string:本次生成的用户输入内容
  • options 类型 object:生成选项(若无额外配置,传空对象 \{\}

返回值类型为 string,即模型生成的文本内容。

const result = await tavo.generate('请用一句话总结今天发生的事情')
console.log(result)

options 字段

options 支持以下字段:

  • context 类型 boolean(默认 false):
    1. true:带当前对话上下文生成(沿用当前聊天状态)
    2. false:与当前对话无关的AI生成请求(默认)
  • preset 类型 number | object(可选):
    1. 直接传预设 ID,例如 12
    2. 传对象时仅识别 id,例如 { id: 12 }
  • settings 类型 object(可选):覆盖本次请求的模型参数

示例:

const text = await tavo.generate(
  '根据最近对话,给我 3 条行动建议',
  {
    context: true,
    preset: { id: 8 },
    settings: {
      temperature: 0.7,
      topP: 0.9,
      maxCompletionTokens: 300,
    },
  },
)

console.log(text)

注意事项

  • 该接口为一次性请求,返回完整文本,不返回流式分片
  • 生成请求会使用当前聊天绑定的模型 API;若当前聊天无可用 API,会抛出异常(请用 try/catch 捕获)

使用示例

将以下内容复制到气泡中,以观察效果:

<h3>生成请求 API 演示</h3>
<pre id="log" style="background: #0006; font-size: 12px; padding: 1em 1.5em; min-height: 80px; max-height: 300px; overflow-y: auto;"></pre>
<button id="btn-generate" onclick="generate()">生成角色卡</button>
<p id="status"></p>
<div id="actions" style="display:none; gap:8px;">
  <button onclick="downloadJson()">下载 JSON 文件</button>
  <button onclick="createCharacter()">直接创建角色卡</button>
</div>
<script>
let generatedCard = null;
const log = (...args) => {
  const text = args.map(v => typeof v === 'string' ? v : JSON.stringify(v, null, 2)).join(' ');
  document.getElementById('log').textContent = text + '\n\n';
};
function setUi(loading, status, showActions = false) {
  document.getElementById('btn-generate').disabled = loading;
  document.getElementById('status').textContent = status;
  document.getElementById('actions').style.display = showActions ? 'flex' : 'none';
}
async function generate() {
  const p = prompt('请输入想要生成的角色特点');
  if (!p) return;
  setUi(true, '生成中...');
  try {
    let text = await tavo.generate(`根据以下信息生成一张角色卡,输出符合 Character Card Spec V3 规范的JSON格式\n${p}`);
    log(text)
    text = text.trim();
    if (text.startsWith('```') && text.endsWith('```')) {
      text = text.replace(/^```[a-zA-Z]*\n?/, '').replace(/```$/, '');
    }
    generatedCard = JSON.parse(text);
    if (generatedCard.mes_example instanceof Array) generatedCard.mes_example = generatedCard.mes_example.join('\n')
    setUi(false, `角色卡 《${generatedCard.name}》 已生成`, true);
  } catch (e) {
    log(e);
    console.log(e);
    setUi(false, `角色卡生成失败`, false)
  }
}
function downloadJson() {
  tavo.utils.export(`${generatedCard.name}.json`, JSON.stringify(generatedCard));
}
async function createCharacter() {
  await tavo.character.create(generatedCard);
}
</script>

🎨 生图

可以通过此接口直接触发一次生图,所有生图接口均为 tavo.image.<method>(...)

发起生图

await tavo.image.generate(<prompt>, <options>)

  • prompt 类型 string:本次生图的提示词(必填,非空)
  • options 类型 object:生图选项(可选)

返回值类型为 string,默认是图片 dataUrl;传入 saveAs 时返回保存后的虚拟路径。两者都可直接用于:

  • <img src="..."> 渲染
const img = await tavo.image.generate('a calico cat sleeping on a keyboard')
const el = document.getElementById('cat')
el.src = img
el.onclick = () => tavo.utils.preview(img)

options 字段

options 支持以下字段,全部可选:

  • size 类型 string(例 "1024x1024"):OpenAI 系平台使用
  • aspectRatio 类型 string(例 "16:9""1:1"):支持宽高比参数的平台使用
  • negativePrompt 类型 string:负面提示词。NovelAI / SD 类生效,OpenAI / Gemini 忽略
  • referenceImages 类型 string[]:参考图数组,用于 img2img。每项可以是 dataUrl,也可以是 tavo.file.save 返回的相对路径(直接传路径更高效,省去读回 dataUrl)。Gemini / OpenAI / Partner(Volink)/ OpenRouter 支持多图;NovelAI 协议限制只取第一张
  • extraBody 类型 object:透传到平台 API 的额外字段(如 seed / guidance_scale / quality 等)
  • saveAs 类型 string(含扩展名,如 'hero.png'):传了就直接落盘,返回虚拟路径而非 dataUrl。等价于 tavo.image.generate(...) + tavo.file.save(saveAs, dataUrl) 两步合一。文件名规则同 tavo.file.save(禁止 / \ : ..,违反会抛出 Error,同名覆盖)
  • scope 类型 string'chat'(默认)| 'global',仅在传了 saveAs 时生效,与 tavo.file.save 的 scope 同义
const wide = await tavo.image.generate('cyberpunk night street, neon', {
  aspectRatio: '16:9',
  negativePrompt: 'low quality, blurry, watermark',
  extraBody: { quality: 'hd' },
})

// 一步生成 + 落盘
const path = await tavo.image.generate('a calico cat', {
  saveAs: 'hero.png',                  // 返回 'files/chat/hero.png'(虚拟路径)
})
imgEl.src = path
tavo.set('hero', path)                  // 路径塞变量,跨克隆/导入聊天免重写

注意事项

  • 该接口为一次性请求,返回完整 dataUrl(或虚拟路径,当传 saveAs 时),不返回流式分片
  • 使用当前聊天绑定的生图 API(与输入框生图入口相同);若当前聊天无可用生图 API,会抛出异常
  • 不会触发提示词扩写(与「生图设置」页的「自动扩写」开关无关;如需扩写,请先用 tavo.generate 转一道)
  • 未传 saveAs 时不会持久化到本地存储,bytes 仅通过返回值送出;传了 saveAs 则直接落盘
  • 不弹出任何用户确认对话框

使用示例

将以下内容复制到气泡中观察效果:

<h3>🎨 生图 API 演示</h3>
<div class="control">
  <button id="btn-generate" onclick="run()">生成图片</button>
  <p id="status"></p>
  <img id="out" onclick="tavo.utils.preview(this.src)" style="max-width: 100%; border-radius: 8px; margin-top: 8px; cursor: pointer;" />
</div>
<script>
async function run() {
  const p = prompt('请输入生图提示词', 'a calico cat sleeping on a keyboard')
  if (!p) return
  const btn = document.getElementById('btn-generate')
  const status = document.getElementById('status')
  const out = document.getElementById('out')
  btn.disabled = true
  status.textContent = '生成中...'
  out.src = ''
  try {
    const img = await tavo.image.generate(p, { aspectRatio: '1:1' })
    out.src = img
    status.textContent = '完成'
  } catch (e) {
    status.textContent = '失败:' + e.message
    tavo.utils.toast('生图失败:' + e.message)
  } finally {
    btn.disabled = false
  }
}
</script>

🔊 TTS

通过当前聊天已有的 TTS 绑定合成并播放文本。

await tavo.tts.play('欢迎回来。', {
  voice: { character: 123 }, // 也可以传 { id: 123 }
  queue: false,
  applyPlaybackRules: false,
})

await tavo.tts.stop()

voice 必须在 characterpersona 中二选一,两者都接受 id 或包含 id 的对象。普通消息 TavoJS 可以省略 voice,使用宿主消息说话人的绑定; 插件 TavoJS 必须显式传入 voice,并通过现有聊天、消息或资料库 API 自行找到 说话人。

play() 在开始播放或进入队列时返回 true;文本为空、目标不存在或目标没有 可用绑定时返回 falsequeueapplyPlaybackRules 默认都是 falsestop() 会停止当前聊天的共享 TTS 并清空等待队列。目前不支持直接传入 voiceId 或 TTS endpoint id。

📁 文件

可以通过此接口把数据持久化到 app 本地存储,所有文件接口均为 tavo.file.<method>(...)

适合把生成的图片、下载的资源、配置文件等存到磁盘,避免把大体积数据(如图片 dataUrl)直接塞进变量或消息内容里。

作用域(scope)

与变量一致,文件也分作用域:

  • chat(默认):聊天作用域,文件随当前聊天保存,聊天删除时一并清理
  • global:全局作用域,跨聊天持久保存,需脚本主动删除

保存文件

await tavo.file.save(<name>, <content>, <options>)

把内容写入存储,返回相对路径字符串,可直接用于 <img src>referenceImages 等。

  • name 类型 string:文件名(含扩展名,如 'avatar.png')。不能含 / \ : ..,否则抛错。同名覆盖。
  • content 类型 string:要保存的内容,见下方「content 与 encoding」
  • options 类型 object(可选):
    • scope'chat'(默认)| 'global'
    • encoding:不传时自动识别;显式可传 'utf8' | 'base64' | 'dataUrl'

content 与 encoding

不传 encoding 时按内容自动识别:

  • data: 开头:当 dataUrl,解码二进制存
  • http:// / https:// 开头:下载远程内容存
  • 其余:当 UTF-8 纯文本存

需要覆盖自动识别时显式传 encoding

  • 'utf8':强制当纯文本(即使内容长得像 dataUrl)
  • 'base64':内容是裸 base64(无 data: 前缀),解码二进制存
  • 'dataUrl':内容是 dataUrl,剥前缀解码存
// 纯文本 / JSON(自动 utf8)
await tavo.file.save('note.md', '# 标题\n正文')
await tavo.file.save('cfg.json', JSON.stringify({ theme: 'dark' }), { scope: 'global' })

// 生成的图片(generate 返回 dataUrl,自动识别)
const dataUrl = await tavo.image.generate('a calico cat')
const path = await tavo.file.save('cat.png', dataUrl)
document.getElementById('out').src = path  // 直接渲染

// 从 URL 下载
await tavo.file.save('report.pdf', 'https://example.com/report.pdf', { scope: 'global' })

// 把 dataUrl 字符串当纯文本存(不解码)
await tavo.file.save('log.txt', dataUrl, { encoding: 'utf8' })

读取文件

await tavo.file.load(<name>, <options>)

读出内容,文件不存在返回 null

  • options.scope'chat'(默认)| 'global'
  • options.encoding'utf8'(默认,返回文本)| 'dataUrl'(返回 dataUrl,可 <img src>)| 'base64'(返回裸 base64)
const text = await tavo.file.load('note.md')                             // 文本
const dataUrl = await tavo.file.load('cat.png', { encoding: 'dataUrl' })  // dataUrl

提示:渲染图片通常不需要 load,直接用 tavo.file.url(name)save 的返回值当 <img src> 即可。load 主要用于读回文本配置,或需要把图片字节做二次处理时。

删除文件

await tavo.file.delete(<name>, <options>)

删除文件,不存在则静默返回。options.scope'chat'(默认)| 'global'

await tavo.file.delete('cat.png')
await tavo.file.delete('logo.png', { scope: 'global' })

文件是否存在

await tavo.file.exists(<name>, <options>)

返回 booleanoptions.scope'chat'(默认)| 'global'

if (await tavo.file.exists('avatar.png')) {
  document.getElementById('out').src = tavo.file.url('avatar.png')
}

拼接渲染路径(同步)

tavo.file.url(<name>, <scope>)

同步返回文件的相对路径(不检测是否存在,永远返回字符串),用于在知道文件名时直接拼出 <img src>

  • scope'chat'(默认)| 'global'
// 已知文件名,直接渲染(无需 await,无需记 save 的返回值)
imgEl.src = tavo.file.url('avatar.png')
imgEl.src = tavo.file.url('logo.png', 'global')

注意事项

  • 文件名不能含 / \ : ..(防路径穿越),违反抛 Error
  • 同名覆盖,无冲突提示
  • chat 作用域文件随聊天删除一并清理;global 作用域需脚本主动 delete
  • 克隆聊天时,原会话的 chat 作用域文件会一并拷贝到新会话;变量与消息里的路径无需重写(路径不绑 chatId,新会话渲染时自动解析)
  • URL 下载不限制白名单 / 大小 / 超时,脚本作者自行担保来源

使用示例

<h3>📁 文件存储演示</h3>
<button onclick="run()">生成并存盘</button>
<p id="status"></p>
<div id="out"></div>
<script>
async function run() {
  const status = document.getElementById('status')
  const out = document.getElementById('out')
  status.textContent = '生成中...'
  try {
    const dataUrl = await tavo.image.generate('a calico cat on a windowsill', { aspectRatio: '1:1' })
    // 存盘,拿短路径(不把大 dataUrl 塞进消息/变量)
    const path = await tavo.file.save('demo-cat.png', dataUrl)
    out.innerHTML = `<img src="${path}" style="max-width:240px;border-radius:8px" />`
    // 路径记到变量,下次直接取
    tavo.set('lastCat', path)
    status.textContent = '已存盘:' + path
  } catch (e) {
    status.textContent = '失败:' + e.message
  }
}
</script>

⌨️ 输入框

可以通过此接口读取或操作聊天输入框,所有输入框接口均为 tavo.input.<method>(...)

读取输入框

await tavo.input.get()

获取当前输入框中的文本内容:

let text = await tavo.input.get()  // 获取当前输入框内容

写入输入框

tavo.input.set(<text>)

覆盖写入输入框内容(会清除原有内容):

tavo.input.set('你好!')  // 将输入框内容替换为"你好!"

追加到输入框

tavo.input.append(<text>)

在输入框现有内容末尾追加文本:

tavo.input.append(' 继续聊吧')  // 在原有内容后追加文字

清空输入框

tavo.input.clear()

清空输入框内容:

tavo.input.clear()

发送消息

tavo.input.send()

通过正常聊天流程发送当前输入,并等待 Tavo 接受或拒绝;不会等待模型回复或生图完成:

tavo.input.set('今天天气不错')
const result = await tavo.input.send()
if (!result.ok) console.log(result.reason, result.text)

成功返回 { ok: true, text }。失败返回 { ok: false, reason, text },其中 reasoncancelledbusyrejected。插件取消时还可能包含 cancelledBymessagetext 是 input hooks 完成并 trim 后的最终输入。

🛠️ 工具

通用工具接口,所有工具接口均为 tavo.utils.<method>(...)

轻量提示

tavo.utils.toast(<text>)

显示一个轻量 toast 提示,数秒后自动消失

打开链接

tavo.utils.openUrl(<url>)

在外部浏览器中打开一个 URL:

tavo.utils.openUrl('https://example.com')

导出文件

tavo.utils.export(<name>, <data>)

将数据导出为文件并触发系统分享/保存。data 可以是 Base64 编码的内容(推荐)或普通文本字符串:

tavo.utils.export('叶离角色卡', btoa('这是一段文本或二进制,调用 btoa 转化为 base64'))  // 传 base64 数据(推荐)
tavo.utils.export('record.txt', '这是一段文本内容')  // 普通文本

全屏图片预览

tavo.utils.preview(<src>)

打开一个全屏的图片查看器(可缩放 / 拖动 / 保存到相册)。src 可以是:

  • data:image/<mime>;base64,... 的 dataUrl(如 tavo.image.generate 的返回值)
  • http:// / https:// URL
  • app 内的文件相对路径(files/<...>

src 必须为非空字符串,否则会抛 Error。打开 viewer 后立即返回,不等用户关闭。

// 预览生图结果
const img = await tavo.image.generate('a calico cat sleeping on a keyboard')
tavo.utils.preview(img)

如果想让气泡中的图片点击后进入全屏预览,请自行给图片绑定点击事件:

<img src="..." onclick="tavo.utils.preview(this.src)" style="cursor:pointer" />

Tavo 不会自动拦截任何 <img> 的点击。装饰图可以不绑定 onclick,可交互图再绑定预览事件,这样脚本作者能明确控制哪些图片可点。

选择器

await tavo.utils.select(<options>, <title?>, <defaultValue?>)

弹出一个原生选择器,等待用户选择后返回所选的 value,取消则返回 null

  • options:选项数组,支持以下三种格式:
    • string[]:字符串数组,value 与显示文本相同
    • { value: string, label: string }[]:对象数组,value 为返回值,label 为显示文本
    • { value: string, label: string, description?: string, subtitle?: string }[]:完整对象,支持副标题与描述
  • title:(可选)选择器标题
  • defaultValue:(可选)默认选中项的 value
// 1. 字符串数组
const fruit = await tavo.utils.select(['苹果', '香蕉', '橙子'], '选择水果')

// 2. 对象数组(value + label)
const lang = await tavo.utils.select([
  { value: 'zh', label: '中文' },
  { value: 'en', label: 'English' },
  { value: 'ja', label: '日本語' },
], '选择语言', 'zh')

// 3. 完整对象(value + label + description + subtitle)
const role = await tavo.utils.select([
  { value: 'warrior', label: '战士', description: '近战物理攻击', subtitle: '推荐新手' },
  { value: 'mage',    label: '法师', description: '远程魔法攻击', subtitle: '高爆发' },
  { value: 'healer',  label: '治疗师', description: '辅助回血',   subtitle: '团队支援' },
], '选择职业', 'mage')

if (role !== null) {
  tavo.utils.toast(`你选择了: ${role}`)
}

📱 App

可以通过此接口读取或操作一些应用属性,所有接口均为 tavo.app.<method>(...)

获得当前 app 版本

await tavo.app.version();  // 字符串:0.77.0
await tavo.app.versionNumber();  // 数字: 770

🏷️ 版本

可以获得当前 API 版本。Tavo 提供不同版本的 API 接口访问,例如 tavo.v1 即为 v1 版本的命名空间。

// 以下方式等效
tavo.get('name')
tavo.v1.get('name')

🔌 兼容性

提供与其他平台的兼容性支持。

触发斜杠命令(SillyTavern 兼容)

triggerSlash(<cmd>)

触发 SillyTavern 风格的斜杠命令,用于兼容从 SillyTavern 迁移过来的脚本:

triggerSlash('/send Hello World | /trigger')

⏳ 持续更新中

TavoJS API 现在还处于早期的 beta 阶段,我们仍在持续建设中,如果你有疑问或好的想法,欢迎到社区中进行反馈。

目录

🧩 TavoJS API📖 概述⚙️ 变量获取变量设置变量更新变量删除变量变量路径消息作用域在提示词中使用变量💬 消息查找消息获取单条消息获取当前消息获取消息总数追加消息更新消息reuseContext 说明删除消息🗨️ 聊天获取当前聊天更新当前聊天设置聊天背景 background聊天对象字段🧙 角色获取所有角色概要获取单个角色按名称查找角色新建角色更新角色导入角色卡删除角色角色对象字段🎭 用户身份获取所有用户身份概要获取单个用户身份按名称查找用户身份新建用户身份更新用户身份删除用户身份用户身份对象字段🎛️ 预设获取所有预设(摘要)获取单个预设按名称查找预设导入预设新建预设更新预设删除预设预设对象字段基础提词字段(BasicPrompts)提词条目字段(PresetEntry)内置条目 identifier 列表📚 世界书获取所有世界书概要获取单个世界书按名称查找世界书导入世界书新建世界书更新世界书删除世界书世界书对象字段条目对象字段(LorebookEntry)🎨 正则获取所有正则(摘要)获取单个正则按名称查找正则导入正则新建正则更新正则删除正则正则对象字段规则条目字段(RegexEntry)🧠 长记忆获取当前记忆更新记忆记忆对象字段✨ 生成请求发起生成options 字段注意事项使用示例🎨 生图发起生图options 字段注意事项使用示例🔊 TTS📁 文件作用域(scope)保存文件content 与 encoding读取文件删除文件文件是否存在拼接渲染路径(同步)注意事项使用示例⌨️ 输入框读取输入框写入输入框追加到输入框清空输入框发送消息🛠️ 工具轻量提示打开链接导出文件全屏图片预览选择器📱 App获得当前 app 版本🏷️ 版本🔌 兼容性触发斜杠命令(SillyTavern 兼容)