-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathindex.js
More file actions
164 lines (144 loc) · 5.3 KB
/
index.js
File metadata and controls
164 lines (144 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* Example plugin -- randomness toolkit (dice + picker)
*
* Copy this folder to start a new plugin:
* cp -r plugins/example plugins/your-plugin
*
* Then edit the tools below. Each tool needs:
* - name: unique identifier (snake_case, no spaces)
* - description: the LLM reads this to decide when to call your tool
* - parameters: JSON Schema describing what the LLM should pass in
* - execute: async function that does the work and returns a result
*/
// ---------------------------------------------------------------------------
// Tool 1: dice_roll
// Shows: optional params with defaults, input validation, context usage
// ---------------------------------------------------------------------------
const diceRoll = {
// Must be unique across all plugins. Collisions are silently skipped.
name: "dice_roll",
// The LLM sees this description and decides whether to call the tool.
// Be specific -- vague descriptions lead to bad tool selection.
description:
"Roll one or more dice with configurable sides. Useful for games, decisions, or tabletop RPGs.",
category: "action",
scope: "always",
// JSON Schema for the parameters the LLM will provide.
// Every property here becomes a named argument in params.
parameters: {
type: "object",
properties: {
sides: {
type: "integer",
description: "Number of sides per die (2-100)",
minimum: 2,
maximum: 100,
},
count: {
type: "integer",
description: "Number of dice to roll (1-20)",
minimum: 1,
maximum: 20,
},
modifier: {
type: "integer",
description: "Bonus or penalty added to the total (can be negative)",
},
},
// No "required" array here -- all params are optional with defaults below.
},
// execute(params, context) is called when the LLM invokes this tool.
//
// params -- the arguments the LLM chose, matching the schema above
// context -- Teleton runtime:
// context.bridge TelegramBridge (send messages, reactions, media)
// context.db SQLite database instance
// context.chatId current chat ID
// context.senderId Telegram user ID of whoever triggered this
// context.isGroup true if group chat, false if DM
// context.config agent config (may be undefined)
//
// Must return { success: true, data: { ... } } or { success: false, error: "..." }
// The data object is serialized to JSON and fed back to the LLM.
execute: async (params, context) => {
const sides = params.sides ?? 6;
const count = params.count ?? 1;
const modifier = params.modifier ?? 0;
// Validate input -- return an error object if something is wrong.
if (sides < 2 || sides > 100) {
return { success: false, error: `sides must be between 2 and 100 (got ${sides})` };
}
if (count < 1 || count > 20) {
return { success: false, error: `count must be between 1 and 20 (got ${count})` };
}
const rolls = [];
for (let i = 0; i < count; i++) {
rolls.push(Math.floor(Math.random() * sides) + 1);
}
const rawTotal = rolls.reduce((sum, r) => sum + r, 0);
const total = rawTotal + modifier;
let formula = `${count}d${sides}`;
if (modifier > 0) formula += `+${modifier}`;
else if (modifier < 0) formula += `${modifier}`;
// Return whatever the LLM needs to build its response.
// Keep it flat and readable -- the LLM parses this JSON.
return {
success: true,
data: {
formula,
rolls,
total,
rolledBy: context.senderId,
isGroupRoll: context.isGroup,
},
};
},
};
// ---------------------------------------------------------------------------
// Tool 2: random_pick
// Shows: required params, array type, input sanitization
// ---------------------------------------------------------------------------
const randomPick = {
name: "random_pick",
description:
"Randomly pick one item from a list of choices. Use for decisions, assignments, or draws.",
category: "action",
scope: "always",
parameters: {
type: "object",
properties: {
choices: {
type: "array",
items: { type: "string" },
minItems: 2,
description: "List of options to choose from (minimum 2)",
},
},
// "required" makes the LLM always provide this param.
required: ["choices"],
},
execute: async (params, context) => {
const { choices } = params;
if (!Array.isArray(choices) || choices.length < 2) {
return { success: false, error: "choices must be an array with at least 2 items" };
}
const valid = choices.filter((c) => typeof c === "string" && c.trim().length > 0);
if (valid.length < 2) {
return { success: false, error: "Need at least 2 non-empty choices" };
}
const picked = valid[Math.floor(Math.random() * valid.length)];
return {
success: true,
data: {
picked,
totalChoices: valid.length,
chatId: context.chatId,
},
};
},
};
// ---------------------------------------------------------------------------
// Export -- Teleton picks up everything in this array.
// One plugin can export as many tools as needed.
// ---------------------------------------------------------------------------
export const tools = [diceRoll, randomPick];