#
STscript 语言参考
#
什么是 STscript?
这是一个简单但功能强大的脚本语言,可用于在不需要严格编程的情况下扩展 SillyTavern 的功能,允许您:
- 创建迷你游戏或速通挑战
- 构建 AI 驱动的聊天洞察
- 释放您的创造力并与他人分享
STscript 是基于斜杠命令引擎构建的,利用命令批处理、数据管道、宏和变量。 这些概念将在以下文档中详细描述。
#
安全注意事项
能力越大,责任越大。在执行脚本之前,请务必仔细检查。
#
Hello, World!
要运行您的第一个脚本,打开任何 SillyTavern 聊天并在聊天输入栏中输入以下内容:
/pass Hello, World! | /echo
您应该会在屏幕顶部看到一条提示消息。现在让我们逐步分析。
脚本是一批命令,每个命令都以斜杠开头,可以带有或不带有命名和未命名参数,并以命令分隔符结束:|
。
命令按顺序依次执行,并在彼此之间传输数据。
/pass
命令接受一个常量值 "Hello, World!" 作为未命名参数并将其写入管道。/echo
命令通过管道从前一个命令接收值并将其显示为提示通知。
提示: 要查看所有可用命令的列表,请在聊天中输入
/help slash
。
由于常量未命名参数和管道是可互换的,我们可以简单地将这个脚本重写为:
/echo Hello, World!
#
用户输入
现在让我们为脚本添加一些交互性。我们将接受用户的输入值并在通知中显示它。
/input Enter your name |
/echo Hello, my name is {{pipe}}
/input
命令用于显示一个输入框,其中包含未命名参数中指定的提示,然后将输出写入管道。- 因为
/echo
已经有一个设置输出模板的未命名参数,我们使用{{pipe}}
宏来指定管道值将被渲染的位置。
#
其他输入/输出命令
/popup (text)
— 显示一个阻塞弹出窗口,支持简单的 HTML 格式,例如:/popup <font color=red>I'm red!</font>
。/setinput (text)
— 用提供的文本替换用户输入栏的内容。/speak voice="name" (text)
— 使用选定的 TTS 引擎和语音映射中的角色名称朗读文本,例如/speak name="Donald Duck" Quack!
。/buttons labels=["a","b"] (text)
— 显示一个带有指定文本和按钮标签的阻塞弹出窗口。labels
必须是 JSON 序列化的字符串数组或包含此类数组的变量名。将点击的按钮标签返回到管道中,如果取消则返回空字符串。文本支持简单的 HTML 格式。
#
/popup
和 /input
的参数
/popup
和 /input
支持以下附加命名参数:
large=on/off
- 增加弹出窗口的垂直大小。默认:off
。wide=on/off
- 增加弹出窗口的水平大小。默认:off
。okButton=string
- 添加自定义"确定"按钮文本的功能。默认:Ok
。rows=number
- (仅用于/input
) 增加输入控件的大小。默认:1。
示例:
/popup large=on wide=on okButton="Accept" Please accept our terms and conditions....
#
/echo
的参数
/echo
支持以下 severity
附加参数值,用于设置显示消息的样式。
warning
error
info
(默认)success
示例:
/echo severity=error Something really bad happened.
#
变量
变量用于在脚本中存储和操作数据,可以使用命令或宏。变量可以是以下类型之一:
- 本地变量 — 保存到当前聊天的元数据中,对其唯一。
- 全局变量 — 保存到 settings.json 中,在整个应用程序中都存在。
/getvar name
或{{getvar::name}}
— 获取本地变量的值。/setvar key=name value
或{{setvar::name::value}}
— 设置本地变量的值。/addvar key=name increment
或{{addvar::name::increment}}
— 将increment
添加到本地变量的值中。/incvar name
或{{incvar::name}}
— 将本地变量的值增加 1。/decvar name
或{{decvar::name}}
— 将本地变量的值减少 1。/getglobalvar name
或{{getglobalvar::name}}
— 获取全局变量的值。/setglobalvar key=name
或{{setglobalvar::name::value}}
— 设置全局变量的值。/addglobalvar key=name
或{{addglobalvar::name:increment}}
— 将increment
添加到全局变量的值中。/incglobalvar name
或{{incglobalvar::name}}
— 将全局变量的值增加 1。/decglobalvar name
或{{decglobalvar::name}}
— 将全局变量的值减少 1。/flushvar name
— 删除本地变量的值。/flushglobalvar name
— 删除全局变量的值。
- 之前未定义的变量的默认值是空字符串,或者如果首次在
/addvar
、/incvar
、/decvar
命令中使用,则为零。 /addvar
命令中的增量如果增量和变量值都可以转换为数字,则执行加法或减法,否则执行字符串连接。- 如果命令参数接受变量名,并且同名的本地和全局变量都存在,则本地变量优先。
- 所有用于变量操作的斜杠命令都将结果值写入管道供下一个命令使用。
- 对于宏,只有 "get"、"inc" 和 "dec" 类型的宏返回值,"add" 和 "set" 则替换为空字符串。
现在,让我们考虑以下示例:
/input What do you want to generate? |
/setvar key=SDinput |
/echo Requesting an image of {{getvar::SDinput}} |
/getvar SDinput |
/imagine
- 用户输入的值保存在名为
SDinput
的本地变量中。 getvar
宏用于在/echo
命令中显示值。getvar
命令用于检索变量的值并通过管道传递。- 该值传递给
/imagine
命令(由图像生成插件提供)作为其输入提示。
由于变量在脚本执行之间保存且不会被清除,您可以在其他脚本中和通过宏引用该变量,它将解析为与示例脚本执行期间相同的值。要确保值被丢弃,请在脚本中添加 /flushvar
命令。
#
数组和对象
变量值可以包含 JSON 序列化的数组或键值对(对象)。
示例:
- 数组:
["apple","banana","orange"]
- 对象:
{"fruits":["apple","banana","orange"]}
以下修改可以应用于命令以处理这些变量:
/len
命令获取数组中的项目数。index=number/string
命名参数可以添加到/getvar
或/setvar
及其全局对应项,以通过数组的从零开始的索引或对象的字符串键获取或设置子值。- 如果在不存在的变量上使用数字索引,该变量将被创建为空数组
[]
。 - 如果在不存在的变量上使用字符串索引,该变量将被创建为空对象
{}
。
- 如果在不存在的变量上使用数字索引,该变量将被创建为空数组
/addvar
和/addglobalvar
命令支持将新值推送到数组类型的变量。
#
流程控制 - 条件语句
您可以使用 /if
命令创建基于定义规则分支执行的条件表达式。
/if left=valueA right=valueB rule=comparison else="(command on false)" "(command on true)"
让我们看看以下示例:
/input What's your favorite drink? |
/if left={{pipe}} right="black tea" rule=eq else="/echo You shall not pass \| /abort" "/echo Welcome to the club, \{\{user\}\}"
这个脚本根据用户输入评估所需值,并根据输入值显示不同的消息。
#
/if
的参数
left
是第一个操作数。让我们称之为 A。right
是第二个操作数。让我们称之为 B。rule
是要应用于操作数的操作。else
是可选的子命令字符串,如果布尔比较结果为 false 则执行。- 未命名参数是如果布尔比较结果为 true 则执行的子命令。
操作数值按以下顺序评估:
- 数字字面量
- 本地变量名
- 全局变量名
- 字符串字面量
命名参数的字符串值可以用引号转义以允许多词字符串。然后丢弃引号。
#
布尔运算
支持以下布尔比较规则。应用于操作数的操作结果为 true 或 false 值。
eq
(等于)=> A = Bneq
(不等于)=> A != Blt
(小于)=> A < Bgt
(大于)=> A > Blte
(小于或等于)=> A <= Bgte
(大于或等于)=> A >= Bnot
(一元否定)=> !Ain
(包含子字符串)=> A 包含 B,不区分大小写nin
(不包含子字符串)=> A 不包含 B,不区分大小写
#
子命令
子命令是包含要执行的斜杠命令列表的字符串。
- 要在子命令中使用命令批处理,命令分隔符字符应该被转义(见下文)。
- 由于宏值在进入条件时执行,而不是在执行子命令时执行,因此可以额外转义宏以延迟其评估到子命令执行时间。
- 子命令执行的结果通过管道传递给
/if
之后的命令。 - 遇到
/abort
命令时中断脚本执行。
/if
命令可以用作三元运算符。
以下示例将在变量 a
等于 5 时将 "true" 字符串传递给下一个命令,否则传递 "false" 字符串。
/if left=a right=5 rule=eq else="/pass false" "/pass true" |
/echo
#
转义序列
#
宏
宏的转义工作方式与以前一样。但是,使用闭包时,您需要转义宏的频率会大大降低。可以转义两个开始的大括号,或者同时转义开始和结束的大括号对。
/echo \{\{char}} |
/echo \{\{char\}\}
#
管道
在闭包中不需要转义管道(当用作命令分隔符时)。在任何想要使用字面管道字符而不是命令分隔符的地方,都需要转义它。
/echo title="a\|b" c\|d |
/echo title=a\|b c\|d |
使用解析器标志 STRICT_ESCAPING
时,不需要在引用值中转义管道。
/parser-flag STRICT_ESCAPING |
/echo title="a|b" c\|d |
/echo title=a\|b c\|d |
#
Quotes
To use a literal quote-character inside a quoted value, the character must be escaped.
/echo title="a \"b\" c" d "e" f
#
Spaces
To use space in the value of a named argument, you either have to surround the value in quote, or escape the space character.
/echo title="a b" c d |
/echo title=a\ b c d
#
Closure Delimiters
If you want to use the character combinations used to mark the beginning or end of a closure, you have to escape the sequence with a single backslash.
/echo \{: |
/echo \:}
#
Pipe Breakers
||
To prevent the previous command's output from being automatically injected as the unnamed argument into the next command, put double pipes between the two commands.
/echo we don't want to pass this on ||
/world
#
Closures
{: ... :}
Closures (block statements, lambdas, anonymous functions, whatever you want to call them) are a series of commands wrapped between {:
and :}
, that are only evaluated once that part of the code is executed.
#
Sub-Commands
Closures make using sub-commands a lot easier and get rid of the need to escape pipes and macros.
// if without closures |
/if left=1 rule=eq right=1
else="
/echo not equal \|
/return 0
"
/echo equal \|
/return \{\{pipe}}
// if with closures |
/if left=1 rule=eq right=1
else={:
/echo not equal |
/return 0
:}
{:
/echo equal |
/return {{pipe}}
:}
#
Scopes
Closures have their own scope and support scoped variables. Scoped variables are declared with /let
, their values set and retrieved with /var
. Another way to get a scoped variable is the {{var::}}
macro.
/let x |
/let y 2 |
/var x 1 |
/var y |
/echo x is {{var::x}} and y is {{pipe}}.
Within a closure, you have access to all variables declared within that same closure or in one of its ancestors. You don't have access to variables declared in a closure's descendants.
If a variable is declared with the same name as a variable that was declared in one of the closure's ancestors, you don't have access to the ancestor variable in this closure and its descendants.
/let x this is root x |
/let y this is root y |
/return {:
/echo called from level-1: x is "{{var::x}}" and y is "{{var::y}}" |
/delay 500 |
/let x this is level-1 x |
/echo called from level-1: x is "{{var::x}}" and y is "{{var::y}}" |
/delay 500 |
/return {:
/echo called from level-2: x is "{{var::x}}" and y is "{{var::y}}" |
/let x this is level-2 x |
/echo called from level-2: x is "{{var::x}}" and y is "{{var::y}}" |
/delay 500
:}()
:}() |
/echo called from root: x is "{{var::x}}" and y is "{{var::y}}"
#
Named Closures
/let x {: ... :} | /:x
Closures can be assigned to variables (only scoped variables) to be called at a later point or to be used as sub-commands.
/let myClosure {:
/echo this is my closure
:} |
/:myClosure
/let myClosure {:
/echo this is my closure |
/delay 500
:} |
/times 3 {{var::myClosure}}
/:
can also be used to execute Quick Replies, as it is just a shorthand for /run
.
/:QrSetName.QrButtonLabel |
/run QrSetName.QrButtonLabel
#
Closure Arguments
Named closures can take named arguments, just like slash commands. The arguments can have default values.
/let myClosure {: a=1 b=
/echo a is {{var::a}} and b is {{var::b}}
:} |
/:myClosure b=10
#
Closures and Piped Arguments
The piped value from a parent closure will not be automatically injected into the first command of a child closure.
You can still explicitly reference the parent's piped value with {{pipe}}
, but if you leave the unnamed argument of the first command inside a closure blank, the value will not be automatically injected.
/* This used to attempt to change the model to "foo"
because the value "foo" from the /echo outside of
the loop was injected into the /model command
inside of the loop.
Now it will simply echo the current model without
attempting to change it.
*|
/echo foo |
/times 2 {:
/model |
/echo |
:} |
/* You can still recreate the old behavior by
explicitly using the {{pipe}} macro.
*|
/echo foo |
/times 2 {:
/model {{pipe}} |
/echo |
:} |
#
Immediately Executed Closures
{: ... :}()
Closures can be immediately executed, meaning they will be replaced with their return value. This is helpful in places where no explicit support for closures exists, and to shorten some commands that would otherwise require a lot of intermediate variables.
// a simple length comparison of two strings without closures |
/len foo |
/var lenOfFoo {{pipe}} |
/len bar |
/var lenOfBar {{pipe}} |
/if left={{var::lenOfFoo}} rule=eq right={{var:lenOfBar}} /echo yay!
// the same comparison with immediately executed closures |
/if left={:/len foo:}() rule=eq right={:/len bar:}() /echo yay!
In addition to running named closures saved inside scoped variables, the /run
command can also be used to execute closures immediately.
/run {:
/add 1 2 3 4 |
:} |
/echo |
#
Comments
// ... | /# ...
A comment is a human-readable explanation or annotation in the script code. Comments don't break pipes.
// this is a comment |
/echo foo |
/# this is also a comment
#
Block Comments
Block comments can be used to quickly comment out multiple commands at once. They will not terminate on a pipe.
/echo foo |
/*
/echo bar |
/echo foobar |
*|
/echo foo again |
#
Flow Control
#
Loops: /while
and /times
If you need to run some command in a loop until a certain condition is met, use the /while
command.
/while left=valueA right=valueB rule=operation guard=on "commands"
On each step of the loop it compares the value of variable A with the value of variable B, and if the condition yields true, then executes any valid slash command enclosed in quotes, otherwise exists the loop. This command doesn't write anything to the output pipe.
#
Arguments for /while
The set of available boolean comparisons, handing of variables, literal values, and subcommands is the same as for the /if
command.
The optional guard
named argument (on
by default) is used to protect against endless loops, limiting the number of iterations to 100.
To disable and allow endless loops, set guard=off
.
This example adds 1 to the value of i
until it reaches 10, then outputs the resulting value (10 in this case).
/setvar key=i 0 |
/while left=i right=10 rule=lt "/addvar key=i 1" |
/echo {{getvar::i}} |
/flushvar i
#
Arguments for /times
Runs a subcommand a specified number of times.
/times (repeats) "(command)"
– any valid slash command enclosed in quotes repeats a number of times, e.g. /setvar key=i 1 | /times 5 "/addvar key=i 1"
adds 1 to the value of "i" 5 times.
- {{timesIndex}} is replaced with the iteration number (zero-based), e.g.
/times 4 "/echo {{timesIndex}}"
echoes the numbers 0 through 4. - Loops are limited to 100 iterations by default, pass
guard=off
to disable.
#
Breaking out of Loops and Closures
/break |
The /break
command can be used to break out of a loop (/while
or /times
) or a closure early. The unnamed argument of /break
can be used to pass a value different from the current pipe along.
/break
is currently implemented in the following commands:
/while
- exits the loop early/times
- exits the loop early/run
(with a closure or closure via variable) - exits the closure early/:
(with a closure) - exits the closure early
/times 10 {:
/echo {{timesIndex}}
/delay 500 |
/if left={{timesIndex}} rule=gt right=3 {:
/break
:} |
:} |
/let x {: iterations=2
/if left={{var::iterations}} rule=gt right=10 {:
/break too many iterations! |
:} |
/times {{var::iterations}} {:
/delay 500 |
/echo {{timesIndex}} |
:} |
:} |
/:x iterations=30 |
/echo the final result is: {{pipe}}
/run {:
/break 1 |
/pass 2 |
:} |
/echo pipe will be one: {{pipe}} |
/let x {:
/break 1 |
/pass 2 |
:} |
/:x |
/echo pipe will be one: {{pipe}} |
#
Math operations
- All of the following operations accept a series of numbers or variable names and output the result to the pipe.
- Invalid operations (such as division by zero), and operations that result in a NaN value or infinity return zero.
- Multiplication, addition, minimum and maximum accept an unlimited number of arguments separated by spaces.
- Subtraction, division, exponentiation, and modulo accept two arguments separated by spaces.
- Sine, cosine, natural logarithm, square root, absolute value, and rounding accept one argument.
List of operations:
/add (a b c d)
– performs an addition of the set of values, e.g./add 10 i 30 j
/mul (a b c d)
– performs a multiplication of the set of values, e.g./mul 10 i 30 j
/max (a b c d)
– returns a maximum from the set of values, e.g./max 1 0 4 k
/min (a b c d)
– return a minimum from the set of values, e.g./min 5 4 i 2
/sub (a b)
– performs a subtraction of two values, e.g./sub i 5
/div (a b)
– performs a division of two values, e.g./div 10 i
/mod (a b)
– performs a modulo operation of two values, e.g./mod i 2
/pow (a b)
– performs a power operation of two values, e.g./pow i 2
/sin (a)
– performs a sine operation of a value, e.g./sin i
/cos (a)
– performs a cosine operation of a value, e.g./cos i
/log (a)
– performs a natural logarithm operation of a value, e.g./log i
/abs (a)
– performs an absolute value operation of a value, e.g./abs -10
/sqrt (a)
– performs a square root operation of a value, e.g./sqrt 9
/round (a)
– performs a rounding to the nearest integer operation of a value, e.g./round 3.14
/rand (round=round|ceil|floor from=number=0 to=number=1)
– returns a random number between from and to, e.g./rand
or/rand 10
or/rand from=5 to=10
. Ranges are inclusive. The returned value will contain a fractional part. Useround
named argument to get an integral value, e.g./rand round=ceil
to round up,round=floor
to round down, andround=round
to round to nearest.
#
Example 1: get an area of a circle with a radius of 50.
/setglobalvar key=PI 3.1415 |
/setvar key=r 50 |
/mul r r PI |
/round |
/echo Circle area: {{pipe}}
#
Example 2: calculate a factorial of 5.
/setvar key=input 5 |
/setvar key=i 1 |
/setvar key=product 1 |
/while left=i right=input rule=lte "/mul product i \| /setvar key=product \| /addvar key=i 1" |
/getvar product |
/echo Factorial of {{getvar::input}}: {{pipe}} |
/flushvar input |
/flushvar i |
/flushvar product
#
Using the LLM
Scripts can make requests to your currently connected LLM API using the following commands:
/gen (prompt)
— generates text using the provided prompt for the selected character and including chat messages./genraw (prompt)
— generates text using just the provided prompt, ignoring the current character and chat./trigger
— triggers a normal generation (equivalent to clicking a "Send" button). If in group chat, you can optionally provide a 1-based group member index or a character name to have them reply, otherwise triggers a group round according to the group settings.
#
Arguments for /gen
and /genraw
/genraw lock=on/off stop=[] instruct=on/off (prompt)
lock
— can beon
oroff
. Specifies whether a user input should be blocked while the generation is in progress. Default:off
.stop
— JSON-serialized array of strings. Adds a custom stop string (if the API supports it) just for this generation. Default: none.instruct
(only/genraw
) — can beon
oroff
. Allows to use instruct formatting on the input prompt (if instruct mode is enabled and the API supports it). Set tooff
to force pure prompts. Default:on
.as
(for Text Completion APIs) — can besystem
(default) orchar
. Defines how the last prompt line will be formatted.char
will use a character name,system
will use no or neutral name.
The generated text is then passed through the pipe to the next command and can be saved to a variable or displaced using the I/O capabilities:
/genraw Write a funny message from Cthulhu about taking over the world. Use emojis. |
/popup <h3>Cthulhu says:</h3><div>{{pipe}}</div>
or to insert the generated message as a response from your character:
/genraw You have been memory wiped, your name is now Lisa and you're tearing me apart. You're tearing me apart Lisa! |
/sendas name={{char}} {{pipe}}
#
Prompt injections
Scripts can add custom LLM prompt injections, making it essentially an equivalent of unlimited Author's Notes.
/inject (text)
— inserts any text into the normal LLM prompt for the current chat, and requires a unique identifier. Saved to chat metadata./listinjects
— shows a list of all prompt injections added by scripts for the current chat in a system message./flushinjects
— deletes all prompt injections added by scripts for the current chat./note (text)
— sets the Author's Note value for the current chat. Saved to chat metadata./interval
— sets the Author's Note insertion interval for the current chat./depth
— sets the Author's Note insertion depth for the in-chat position./position
— sets the Author's Note position for the current chat.
#
Arguments for /inject
/inject id=IdGoesHere position=chat depth=4 My prompt injection
id
— an identifier string or a reference to a variable. Consequent calls of/inject
with the same ID will overwrite the previous text injection. Required argument.position
— sets a position for the injection. Default:after
. Possible values:after
: after the main prompt.before
: before main prompt.chat
: in-chat.
depth
— sets an injection depth for the in-chat position. 0 means insertion after the last message, 1 - before the last message, etc. Default: 4.- Unnamed argument is a text to be injected. An empty string will unset the previous value for the provided identifier.
#
Access chat messages
#
Read messages
You can access messages in the currently selected chat using the /messages
command.
/messages names=on/off start-finish
- The
names
argument is used to specify whether you want to include character names or not, default:on
. - In an unnamed argument, it accepts a message index or range in the
start-finish
format. Ranges are inclusive! - If the range is unsatisfiable, i.e. an invalid index or more messages than exist are requested, then an empty string is returned.
- Messages that are hidden from the prompt (denoted by the ghost icon) are excluded from the output.
- If you want to know the index of the latest message, use the
{{lastMessageId}}
macro, and{{lastMessage}}
will get you the message itself.
To calculate the start index for a range, for example, when you need to get the last N messages, use variable subtraction. This example will get you 3 last messages in the chat:
/setvar key=start {{lastMessageId}} |
/addvar key=start -2 |
/messages names=off {{getvar::start}}-{{lastMessageId}} |
/setinput
#
Send messages
A script can send messages as either a user, character, persona, neutral narrator, or add comments.
/send (text)
— adds a message as the currently selected persona./sendas name=charname (text)
— adds a message as any character, matching by their name.name
argument is required. Use the{{char}}
macro to send as the current character./sys (text)
— adds a message from the neutral narrator that doesn't belong to the user or character. The displayed name is purely cosmetic and can be customized with the/sysname
command./comment (text)
— adds a hidden comment that is displayed in the chat but is not visible to the prompt./addswipe (text)
— adds a swipe to the last character message. Can't add a swipe to the user or hidden messages./hide (message id or range)
— hides one or several messages from the prompt based on the provided message index or inclusive range in thestart-finish
format./unhide (message id or range)
— returns one or several messages to the prompt based on the provided message index or inclusive range in thestart-finish
format.
/send
, /sendas
, /sys
, and /comment
commands optionally accept a named argument at
with a zero-based numeric value (or a variable name that contains such a value) that specifies an exact position of message insertion. By default new messages are inserted at the end of the chat log.
This will insert a user message at the beginning of the conversation history:
/send at=0 Hi, I use Linux.
#
Delete messages
These commands are potentially destructive and have no "undo" function. Check the /backups/ folder if you accidentally deleted something important.
/cut (message id or range)
— cuts one or several messages from the chat based on the provided message index or inclusive range in thestart-finish
format./del (number)
— deletes last N messages from the chat./delswipe (1-based swipe id)
— deletes a swipe from the last character message based on the provided 1-based swipe ID./delname (character name)
— deletes all messages in the current chat that belong to a character with the specified name./delchat
— deletes the current chat.
#
World Info commands
World Info (also known as Lorebook) is a highly utilitarian tool for dynamically inserting data into the prompt. See the dedicated page for more detailed explanation: World Info.
/getchatbook
– gets a name of the chat-bound World Info file or create a new one if was unbound, and pass it down the pipe./findentry file=bookName field=fieldName [text]
– finds a UID of the record from the specified file (or a variable pointing to a file name) using fuzzy matching of a field value with the provided text (default field:key
) and passes the UID down the pipe, e.g./findentry file=chatLore field=key Shadowfang
./getentryfield file=bookName field=field [UID]
– gets a field value (default field:content
) of the record with the UID from the specified World Info file (or a variable pointing to a file name) and passes the value down the pipe, e.g./getentryfield file=chatLore field=content 123
./setentryfield file=bookName uid=UID field=field [text]
– sets a field value (default field:content
) of the record with the UID (or a variable pointing to UID) from the specified World Info file (or a variable pointing to a file name). To set multiple values for key fields, use a comma-delimited list as a text value, e.g./setentryfield file=chatLore uid=123 field=key Shadowfang,sword,weapon
./createentry file=bookName key=keyValue [content text]
– creates a new record in the specified file (or a variable pointing to a file name) with the key and content (both of these arguments are optional) and passes the UID down the pipe, e.g./createentry file=chatLore key=Shadowfang The sword of the king
.
#
Valid entry fields
Logic values
- 0 = AND ANY
- 1 = NOT ALL
- 2 = NOT ANY
- 3 = AND ALL
Position values
- 0 = before main prompt
- 1 = after main prompt
- 2 = top of Author's Note
- 3 = bottom of Author's Note
- 4 = in-chat at depth
- 5 = top of example messages
- 6 = bottom of example messages
Role values (Position = 4 only)
- 0 = System
- 1 = User
- 2 = Assistant
#
Example 1: Read a content from the chat lorebook by key
/getchatbook | /setvar key=chatLore |
/findentry file={{getvar::chatLore}} field=key Shadowfang |
/getentryfield file={{getvar::chatLore}} field=key |
/echo
#
Example 2: Create a chat lorebook entry with key and content
/getchatbook | /setvar key=chatLore |
/createentry file={{getvar::chatLore}} key="Milla" Milla Basset is a friend of Lilac and Carol. She is a hush basset puppy who possesses the power of alchemy. |
/echo
#
Example 3: Expand an existing lorebook entry with new information from the chat
/getchatbook | /setvar key=chatLore |
/findentry file={{getvar::chatLore}} field=key Milla |
/setvar key=millaUid |
/getentryfield file={{getvar::chatLore}} field=content |
/setvar key=millaContent |
/gen lock=on Tell me more about Milla Basset based on the provided conversation history. Incorporate existing information into your reply: {{getvar::millaContent}} |
/setvar key=millaContent |
/echo New content: {{pipe}} |
/setentryfield file={{getvar::chatLore}} uid=millaUid field=content {{getvar::millaContent}}
#
Text manipulation
There's a variety of useful text manipulation utility commands to be used in various script scenarios.
/trimtokens
— trims the input to the specified number of text tokens from the start or from the end and outputs the result to the pipe./trimstart
— trims the input to the start of the first complete sentence and outputs the result to the pipe./trimend
— trims the input to the end of the last complete sentence and outputs the result to the pipe./fuzzy
— performs fuzzy matching of the input text to the list of strings, outputting the best string match to the pipe./regex name=scriptName [text]
— executes a regex script from the Regex extension for the specified text. The script must be enabled.
#
Arguments for /trimtokens
/trimtokens limit=number direction=start/end (input)
direction
sets the direction for trimming, which can be eitherstart
orend
. Default:end
.limit
sets the amount of tokens to left in the output. Can also specify a variable name containing the number. Required argument.- Unnamed argument is the input text to be trimmed.
#
Arguments for /fuzzy
/fuzzy list=["candidate1","candidate2"] (input)
list
is a JSON-serialized array of strings containing the candidates. Can also specify a variable name containing the list. Required argument.- Unnamed argument is the input text to be matched. Output is one of the candidates matching the input most closely.
#
Autocomplete
- Autocomplete is enabled both on the chat input, and the large Quick Reply editor.
- Autocomplete works anywhere in your input. Even with multiple piped commands and nested closures.
- Autocomplete supports three ways of looking up matching commands (User Settings -> STscript Matching).
- Starts with The "old" way. Only commands that begin exactly with the typed value will show up.
- Includes All commands that include the type value will show up. Example: When entering
/delete
, the commands/qr-delete
and/qr-set-delete
will show up in the autocomplete list (/qr-delete, /qr-set-delete). - Fuzzy All commands that can be fuzzy-matched against the typed value will show up. Example: When entering
/seas
, the command/sendas
will show up in the autocomplete list (/sendas).
- Command arguments are supported by autocomplete as well. The list will show up for required arguments automatically. For optional arguments, press Ctrl+Space to open the list of available options.
- When entering
/:
to execute a closure or QR, autocomplete will show a list of scoped variables and QRs. - Autocomplete has limited support for macros (in slash commands). Type
{{
to get a list of available macros. - Use the up and down arrow keys to select an option from the list of autocomplete options.
- Press Enter or Tab or click on an option to place the option at the cursor.
- Press Escape to close the autocomplete list.
- Press Ctrl+Space to open the autocomplete list or toggle the selected option's details.
#
Parser Flags
/parser-flag
The parser accepts flags to modify its behavior. These flags can be toggled on and off at any point in a script and all following input will be evaluated accordingly.
You can set your default flags in user settings.
#
Strict Escaping
/parser-flag STRICT_ESCAPING on |
Changes with STRICT_ESCAPING
enabled are as follows.
#
Pipes
Pipes don't need to be escaped in quoted values.
/echo title="a|b" c\|d
#
Backslashes
A backslash in front of a symbol can be escaped to provide the literal backslash followed by the functional symbol.
// this will echo "foo \", then echo "bar" |
/echo foo \\|
/echo bar
/echo \\|
/echo \\\|
#
Replace Variable Macros
/parser-flag REPLACE_GETVAR on |
This flag helps to avoid double-substitutions when the variable values contain text that could be interpreted as macros. The {{var::}}
macros get substituted last and no further substitutions happen on the resulting text / variable value.
Replaces all {{getvar::}}
and {{getglobalvar::}}
macros with {{var::}}
.
Behind the scenes, the parser will insert a series of command executors before the command with the replaced macros:
- call
/let
to save the current{{pipe}}
to a scoped variable - call
/getvar
or/getglobalvar
to get the variable used in the macro - call
/let
to save the retrieved variable to a scoped variable - call
/return
with the saved{{pipe}}
value to restore the correct piped value for the next command
// the following will echo the last message's id / number |
/setvar key=x \{\{lastMessageId}} |
/echo {{getvar::x}}
// this will echo the literal text {{lastMessageId}} |
/parser-flag REPLACE_GETVAR |
/setvar key=x \{\{lastMessageId}} |
/echo {{getvar::x}}
#
Quick Replies: script library and auto-execution
Quick Replies is a built-in SillyTavern extension that provides an easy way to store and execute your scripts.
#
Configuring Quick Replies
In order to get started, enable open the extensions panel (stacked blocks icon), and expand the Quick Replies menu.
Quick Replies are disabled by default, you need to enable them first. Then you will see a bar appearing above your chat input bar.
You can set the displayed button text label (we recommend using emojis for brevity) and the script that will be executed when you click the button.
The number of buttons is controlled by the Number of slots settings (max = 100), adjust it according to your needs and click "Apply" when done.
Inject user input automatically recommended to be disabled when using STscript, otherwise it may interfere with your inputs, use the {{input}}
macro to get the current value of the input bar in scripts instead.
Quick Reply presets allow to have multiple sets of predefined Quick Replies and switch between manually or by using the /qrset (name of set)
command.
Don't forget to click "Update" before switching to a different set to write your changes to the currently used preset!
#
Manual execution
Now you can add your first script to the library. Pick any free slot (or create one), type "Click me" into the left box to set the label, then paste this into the right box:
/addvar key=clicks 1 |
/if left=clicks right=5 rule=eq else="/echo Keep going..." "/echo You did it! \| /flushvar clicks"
Then click 5 times on the button that appeared above the chat bar.
Every click increments the variable clicks
by one and displays a different message when the value equals 5 and resets the variable.
#
Automatic execution
Open the modal menu by clicking the ⋮
button for the created command.
In this menu you can do the following:
- Edit the script in a convenient full-screen editor
- Hide the button from the chat bar, making it accessible only for auto-execution.
- Enable automatic execution on one or more of the following conditions:
- App startup
- Sending a user message to the chat
- Receiving an AI message in the chat
- Opening a character or group chat
- Triggering a reply from a group member
- Activating a World Info entry using the same Automation ID
- Provide a custom tool tip for the quick reply (text displayed when hovering over the quick reply in your UI)
- Execute the script for test purposes
Commands are executed automatically only if the Quick Replies extension is enabled.
For example, you can display a message after sending five user messages by adding the following script and setting it to auto-execute on the user message.
/addvar key=usercounter 1 |
/echo You've sent {{pipe}} messages. |
/if left=usercounter right=5 rule=gte "/echo Game over! \| /flushvar usercounter"
#
Debugger
A basic debugger exists inside the expanded Quick Reply editor. Set breakpoints with /breakpoint |
anywhere in your script. When executing the script from the QR editor, the execution will be interrupted at that point, allowing you to examine the currently available variables, pipe, command arguments, and more, and to step through the rest of the code one by one.
/let x {: n=1
/echo n is {{var::n}} |
/mul n n |
:} |
/breakpoint |
/:x n=3 |
/echo result is {{pipe}} |
#
Calling procedures
A /run
command can call scripts defined in the Quick Replies by their label, basically providing the ability to define procedures and return results from them. This allows to have reusable script blocks that other scripts could reference. The last result from the procedure's pipe is passed to the next command after it.
/run ScriptLabel
Let's create two Quick Replies:
Label:
GetRandom
Command:
/pass {{roll:d100}}
Label:
GetMessage
Command:
/run GetRandom | /echo Your lucky number is: {{pipe}}
Clicking on the GetMessage
button will call the GetRandom
procedure which will resolve the {{roll}}
macro and pass the number to the caller, displaying it to the user.
- Procedures do not accept named or unnamed arguments, but can reference the same variables as the caller.
- Avoid recursion when calling procedures as it may produce the "call stack exceeded" error if handled unadvisedly.
#
Calling procedures from a different Quick Reply preset
You can call a procedure from a different quick reply preset using the a.b
syntax, where a = QR preset name and b = QR label name
/run QRpreset1.QRlabel1
By default, the system will first look for a quick reply label a.b
, so if one of your labels is literally "QRpreset1.QRlabel1" it will try to run that. If no such label is found, it will search for a QR preset name "QRpreset1" with a QR labeled "QRlabel1".
#
Quick Replies management commands
#
Create Quick Reply
/qr-create (arguments, [message])
– creates a new Quick Reply, example:/qr-create set=MyPreset label=MyButton /echo 123
Arguments:
label
- string - text on the button, e.g.,label=MyButton
set
- string - name of the QR set, e.g.,set=PresetName1
hidden
- bool - whether the button should be hidden, e.g.,hidden=true
startup
- bool - auto execute on app startup, e.g.,startup=true
user
- bool - auto execute on user message, e.g.,user=true
bot
- bool - auto execute on AI message, e.g.,bot=true
load
- bool - auto execute on chat load, e.g.,load=true
title
- bool - title / tooltip to be shown on button, e.g.,title="My Fancy Button"
#
Delete Quick Reply
/qr-delete (set=string [label])
– deletes Quick Reply
#
Update Quick Reply
/qr-update (arguments, [message])
– updates Quick Reply, example:/qr-update set=MyPreset label=MyButton newlabel=MyRenamedButton /echo 123
Arguments:
newlabel
- string - new text fort the button, e.g.newlabel=MyRenamedButton
label
- string - text on the button, e.g.,label=MyButton
set
- string - name of the QR set, e.g.,set=PresetName1
hidden
- bool - whether the button should be hidden, e.g.,hidden=true
startup
- bool - auto execute on app startup, e.g.,startup=true
user
- bool - auto execute on user message, e.g.,user=true
bot
- bool - auto execute on AI message, e.g.,bot=true
load
- bool - auto execute on chat load, e.g.,load=true
title
- bool - title / tooltip to be shown on button, e.g.,title="My Fancy Button"
#
qr-get
- retrieves all of a Quick Reply's properties, eample:/qr-get set=myQrSet id=42
#
Create or update QR preset
/qr-presetupdate (arguments [label])
or/qr-presetadd (arguments [label])
Arguments:
enabled
- bool - enable or disable the presetnosend
- bool - disable send / insert in user input (invalid for slash commands)before
- bool - place QR before user inputslots
- int - number of slotsinject
- bool - inject user input automatically (if disabled use{{input}}
)
Create a new preset (overrides existing ones), example: /qr-presetadd slots=3 MyNewPreset
#
Add QR context menu
/qr-contextadd (set=string label=string chain=bool [preset name])
– add context menu preset to a QR, example:/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset
#
Remove all context menus
/qr-contextclear (set=string [label])
– remove all context menu presets from a QR, example:/qr-contextclear set=MyPreset MyButton
#
Remove one context menu
/qr-contextdel (set=string label=string [preset name])
– remove context menu preset from a QR, example:/qr-contextdel set=MyPreset label=MyButton MyOtherPreset
#
Quick Reply value escaping
|{}
can be escaped with backslash in the QR message / command.
For example, use /qr-create label=MyButton /getvar myvar \| /echo \{\{pipe\}\}
to create a QR that calls /getvar myvar | /echo {{pipe}}
.
#
Extension commands
SillyTavern extensions (both built-in, downloadable and third-party) can add their own slash command. Below is just an example of the capabilities in the official extensions. The list may be incomplete, make sure to check /help slash
for the most complete list of available commands.
/websearch (query)
— searches snippets of the web pages online for the specified query and returns the result into the pipe. Provided by the Web Search extension./imagine (prompt)
— generates an image using the provided prompt. Provided by the Image Generation extension./emote (sprite)
— sets a sprite for the active character by fuzzy matching its name. Provided by the Character Expressions extension./costume (subfolder)
— sets a sprite set override for the active character. Provided by the Character Expressions extension./music (name)
— force changes a played background music file by its name. Provided by the Dynamic Audio extension./ambient (name)
— force changes a played ambient sound file by its name. Provided by the Dynamic Audio extension./roll (dice formula)
— adds a hidden message to the chat with the result of a dice roll. Provided by the D&D Dice extension.
#
UI interaction
Scripts can also interact with SillyTavern's UI: navigate through the chats or change styling parameters.
#
Character navigation
/random
— opens a chat with the random character./go (name)
— opens a chat with the character of the specified name. First, searches for the exact name match, then by a prefix, then by a substring.
#
UI styling
/bubble
— sets the message style to the "bubble chat" style./flat
— sets the message style to the "flat chat" style./single
— sets the message style to the "single document" style./movingui (name)
— activates a MovingUI preset by name./resetui
— resets the MovingUI panels state to their original positions./panels
— toggles the UI panels visibility: top bar, left and right drawers./bg (name)
— finds and sets a background using fuzzy names matching. Respect the chat background lock state./lockbg
— locks the background image for the current chat./unlockbg
— unlocks the background image for the current chat.
#
More examples
#
Generate chat summary (by @IkariDevGIT)
/setglobalvar key=summaryPrompt Summarize the most important facts and events that have happened in the chat given to you in the Input header. Limit the summary to 100 words or less. Your response should include nothing but the summary. |
/setvar key=tmp |
/messages 0-{{lastMessageId}} |
/trimtokens limit=3000 direction=end |
/setvar key=s1 |
/echo Generating, please wait... |
/genraw lock=on instruct=off {{instructInput}}{{newline}}{{getglobalvar::summaryPrompt}}{{newline}}{{newline}}{{instructInput}}{{newline}}{{getvar::s1}}{{newline}}{{newline}}{{instructOutput}}{{newline}}The chat summary:{{newline}} |
/setvar key=tmp |
/echo Done! |
/setinput {{getvar::tmp}} |
/flushvar tmp |
/flushvar s1
#
Buttons popup usage
/setglobalvar key=genders ["boy", "girl", "other"] |
/buttons labels=genders Who are you? |
/echo You picked: {{pipe}}
#
Get Nth Fibonacci's number (using Binet's formula)
Hint: Set value of
fib_no
to the desired number
/setvar key=fib_no 5 |
/pow 5 0.5 | /setglobalvar key=SQRT5 |
/setglobalvar key=PHI 1.618033 |
/pow PHI fib_no | /div {{pipe}} SQRT5 |
/round |
/echo {{getvar::fib_no}}th Fibonacci's number is: {{pipe}}
#
Recursive Factorial (using closures)
/let fact {: n=
/if left={{var::n}} rule=gt right=1
else={:
/return 1
:}
{:
/sub {{var::n}} 1 |
/:fact n={{pipe}} |
/mul {{var::n}} {{pipe}}
:}
:} |
/input Calculate factorial of: |
/let n {{pipe}} |
/:fact n={{var::n}} |
/echo factorial of {{var::n}} is {{pipe}}