Skip to content

Commit f8d81c3

Browse files
committed
feat: ai plugin
1 parent a254b43 commit f8d81c3

File tree

15 files changed

+746
-4
lines changed

15 files changed

+746
-4
lines changed

apps/test-bot/commandkit.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { legacy } from '@commandkit/legacy';
33
import { i18n } from '@commandkit/i18n';
44
import { devtools } from '@commandkit/devtools';
55
import { cache } from '@commandkit/cache';
6+
import { ai } from '@commandkit/ai';
67

78
export default defineConfig({
89
plugins: [
910
i18n(),
1011
legacy({ skipBuiltInValidations: true }),
1112
devtools(),
1213
cache(),
14+
ai(),
1315
],
1416
});

apps/test-bot/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@
1010
"node": "node"
1111
},
1212
"dependencies": {
13+
"@ai-sdk/google": "^1.2.19",
14+
"@commandkit/ai": "workspace:*",
1315
"@commandkit/cache": "workspace:*",
1416
"@commandkit/devtools": "workspace:*",
1517
"@commandkit/i18n": "workspace:*",
1618
"@commandkit/legacy": "workspace:*",
1719
"commandkit": "workspace:*",
1820
"discord.js": "^14.19.1",
19-
"dotenv": "^16.4.7"
21+
"dotenv": "^16.4.7",
22+
"zod": "^3.25.56"
2023
},
2124
"devDependencies": {
2225
"tsx": "^4.7.0"
2326
}
24-
}
27+
}

apps/test-bot/src/ai.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { createGoogleGenerativeAI } from '@ai-sdk/google';
2+
import { configureAI } from '@commandkit/ai';
3+
4+
const google = createGoogleGenerativeAI({
5+
apiKey: process.env.GOOGLE_API_KEY,
6+
});
7+
8+
const model = google.languageModel('gemini-2.0-flash');
9+
10+
configureAI({
11+
selectAiModel: async () => {
12+
return { model };
13+
},
14+
messageFilter: async (message) => {
15+
return (
16+
message.inGuild() && message.mentions.users.has(message.client.user.id)
17+
);
18+
},
19+
});

apps/test-bot/src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Client } from 'discord.js';
22
import { Logger, commandkit } from 'commandkit';
3+
import './ai';
34

45
const client = new Client({
56
intents: [

apps/test-bot/src/app/commands/(leveling)/xp.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
import { ChatInputCommandContext, CommandData } from 'commandkit';
2-
import { database } from '../../../database/store.ts';
2+
import { database } from '@/database/store.ts';
33
import { cacheTag } from '@commandkit/cache';
4+
import { AiConfig, AiContext } from '@commandkit/ai';
5+
import { z } from 'zod';
46

57
export const command: CommandData = {
68
name: 'xp',
79
description: 'This is an xp command.',
810
};
911

12+
export const aiConfig: AiConfig = {
13+
description: 'Get the XP of a user in a guild.',
14+
parameters: z.object({
15+
guildId: z.string().describe('The ID of the guild.'),
16+
userId: z.string().describe('The ID of the user.'),
17+
}),
18+
};
19+
1020
async function getUserXP(guildId: string, userId: string) {
1121
'use cache';
1222

@@ -39,3 +49,26 @@ export async function chatInput({ interaction }: ChatInputCommandContext) {
3949
],
4050
});
4151
}
52+
53+
export async function ai(ctx: AiContext) {
54+
const message = ctx.message;
55+
56+
if (!message.inGuild()) {
57+
return {
58+
error: 'This tool can only be used in a guild.',
59+
};
60+
}
61+
62+
const { guildId, userId } = ctx.params as {
63+
guildId: string;
64+
userId: string;
65+
};
66+
67+
const xp = await getUserXP(guildId, userId);
68+
69+
return {
70+
userId,
71+
guildId,
72+
xp,
73+
};
74+
}

packages/ai/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# `@commandkit/ai`
2+
3+
Supercharge your CommandKit project with AI capabilities.
4+
5+
## Installation
6+
7+
```bash
8+
npm install @commandkit/ai
9+
```
10+
11+
## Usage
12+
13+
```ts
14+
import { ai } from '@commandkit/ai';
15+
16+
export default defineConfig({
17+
plugins: [ai()],
18+
})
19+
```

packages/ai/package.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@commandkit/ai",
3+
"version": "0.1.0",
4+
"description": "Supercharge your CommandKit bot with AI capabilities",
5+
"files": [
6+
"dist"
7+
],
8+
"main": "dist/index.js",
9+
"types": "dist/index.d.ts",
10+
"scripts": {
11+
"lint": "tsc --noEmit",
12+
"build": "tsc"
13+
},
14+
"repository": {
15+
"type": "git",
16+
"url": "git+https://github.com/underctrl-io/commandkit.git"
17+
},
18+
"keywords": [
19+
"commandkit",
20+
"ai"
21+
],
22+
"author": "twilight <[email protected]>",
23+
"license": "MIT",
24+
"bugs": {
25+
"url": "https://github.com/underctrl-io/commandkit/issues"
26+
},
27+
"homepage": "https://github.com/underctrl-io/commandkit#readme",
28+
"devDependencies": {
29+
"commandkit": "workspace:*",
30+
"discord.js": "^14.19.3",
31+
"tsconfig": "workspace:*",
32+
"typescript": "^5.7.3"
33+
},
34+
"dependencies": {
35+
"ai": "^4.3.16",
36+
"zod": "^3.25.48"
37+
}
38+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Message } from 'discord.js';
2+
import { AsyncLocalStorage } from 'node:async_hooks';
3+
import { AiContext } from './context';
4+
5+
const worker = new AsyncLocalStorage<{ message: Message; ctx: AiContext }>();
6+
7+
export function getAiWorkerContext(): { message: Message; ctx: AiContext } {
8+
const ctx = worker.getStore();
9+
10+
if (!ctx) {
11+
throw new Error(
12+
'AI context is not available. Ensure you are using AI in a CommandKit environment.',
13+
);
14+
}
15+
16+
return ctx;
17+
}
18+
19+
export function runInAiWorkerContext<R, F extends (...args: any[]) => R>(
20+
ctx: AiContext,
21+
message: Message,
22+
callback: F,
23+
): R {
24+
return worker.run({ message, ctx }, callback);
25+
}

packages/ai/src/context.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { CommandKit } from 'commandkit';
2+
import { Client, Message } from 'discord.js';
3+
4+
export interface AiContextOptions<
5+
T extends Record<string, unknown> = Record<string, unknown>,
6+
> {
7+
message: Message;
8+
params: T;
9+
commandkit: CommandKit;
10+
}
11+
12+
export class AiContext<
13+
T extends Record<string, unknown> = Record<string, unknown>,
14+
> {
15+
public params!: T;
16+
public message!: Message;
17+
public client!: Client;
18+
public commandkit!: CommandKit;
19+
20+
public constructor(options: AiContextOptions<T>) {
21+
this.params = options.params;
22+
this.message = options.message;
23+
this.commandkit = options.commandkit;
24+
this.client = options.commandkit.client;
25+
}
26+
27+
public setParams(params: T): void {
28+
this.params = params;
29+
}
30+
}

packages/ai/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { AiPlugin } from './plugin';
2+
import { AiPluginOptions } from './types';
3+
4+
export function ai(options?: AiPluginOptions) {
5+
return new AiPlugin(options ?? {});
6+
}
7+
8+
export * from './types';
9+
export * from './plugin';
10+
export * from './context';

0 commit comments

Comments
 (0)