mirror of
https://github.com/block/goose.git
synced 2026-06-02 06:19:33 +02:00
feat: let AskAI Discord bot see channels in the server (#7480)
Signed-off-by: The-Best-Codes <106822363+The-Best-Codes@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
"@ai-sdk/anthropic": "^3.0.46",
|
"@ai-sdk/anthropic": "^3.0.46",
|
||||||
"ai": "^6.0.62",
|
"ai": "^6.0.62",
|
||||||
"consola": "^3.4.2",
|
"consola": "^3.4.2",
|
||||||
|
"dedent": "^1.7.1",
|
||||||
"discord.js": "^14.25.1",
|
"discord.js": "^14.25.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"marked": "^17.0.1",
|
"marked": "^17.0.1",
|
||||||
@@ -127,6 +128,8 @@
|
|||||||
|
|
||||||
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||||
|
|
||||||
|
"dedent": ["dedent@1.7.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg=="],
|
||||||
|
|
||||||
"discord-api-types": ["discord-api-types@0.38.38", "", {}, "sha512-7qcM5IeZrfb+LXW07HvoI5L+j4PQeMZXEkSm1htHAHh4Y9JSMXBWjy/r7zmUCOj4F7zNjMcm7IMWr131MT2h0Q=="],
|
"discord-api-types": ["discord-api-types@0.38.38", "", {}, "sha512-7qcM5IeZrfb+LXW07HvoI5L+j4PQeMZXEkSm1htHAHh4Y9JSMXBWjy/r7zmUCOj4F7zNjMcm7IMWr131MT2h0Q=="],
|
||||||
|
|
||||||
"discord.js": ["discord.js@14.25.1", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g=="],
|
"discord.js": ["discord.js@14.25.1", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g=="],
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
type OmitPartialGroupDMChannel,
|
type OmitPartialGroupDMChannel,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import { answerQuestion } from "../utils/ai";
|
import { answerQuestion } from "../utils/ai";
|
||||||
|
import { buildServerContext } from "../utils/discord/server-context";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -17,6 +18,8 @@ export default {
|
|||||||
if (message.author.bot) return;
|
if (message.author.bot) return;
|
||||||
|
|
||||||
const questionChannelId = process.env.QUESTION_CHANNEL_ID;
|
const questionChannelId = process.env.QUESTION_CHANNEL_ID;
|
||||||
|
const guild = message.guild;
|
||||||
|
const serverContext = guild ? await buildServerContext(guild) : "";
|
||||||
|
|
||||||
// Handle messages in threads
|
// Handle messages in threads
|
||||||
if (message.channel.isThread()) {
|
if (message.channel.isThread()) {
|
||||||
@@ -72,6 +75,7 @@ export default {
|
|||||||
thread: message.channel,
|
thread: message.channel,
|
||||||
userId: message.author.id,
|
userId: message.author.id,
|
||||||
messageHistory: sortedMessages,
|
messageHistory: sortedMessages,
|
||||||
|
serverContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.verbose(
|
logger.verbose(
|
||||||
@@ -105,6 +109,7 @@ export default {
|
|||||||
thread,
|
thread,
|
||||||
userId: message.author.id,
|
userId: message.author.id,
|
||||||
statusMessage,
|
statusMessage,
|
||||||
|
serverContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.verbose(`Answered question for ${message.author.username}`);
|
logger.verbose(`Answered question for ${message.author.username}`);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"@ai-sdk/anthropic": "^3.0.46",
|
"@ai-sdk/anthropic": "^3.0.46",
|
||||||
"ai": "^6.0.62",
|
"ai": "^6.0.62",
|
||||||
"consola": "^3.4.2",
|
"consola": "^3.4.2",
|
||||||
|
"dedent": "^1.7.1",
|
||||||
"discord.js": "^14.25.1",
|
"discord.js": "^14.25.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"marked": "^17.0.1",
|
"marked": "^17.0.1",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Message, ThreadChannel } from "discord.js";
|
|||||||
import { model } from "../../clients/ai";
|
import { model } from "../../clients/ai";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { chunkMarkdown } from "./chunk-markdown";
|
import { chunkMarkdown } from "./chunk-markdown";
|
||||||
import { MAX_STEPS, SYSTEM_PROMPT } from "./system-prompt";
|
import { MAX_STEPS, buildSystemPrompt } from "./system-prompt";
|
||||||
import { ToolTracker } from "./tool-tracker";
|
import { ToolTracker } from "./tool-tracker";
|
||||||
import { aiTools } from "./tools";
|
import { aiTools } from "./tools";
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ export interface AnswerQuestionOptions {
|
|||||||
userId: string;
|
userId: string;
|
||||||
messageHistory?: MessageHistoryItem[];
|
messageHistory?: MessageHistoryItem[];
|
||||||
statusMessage?: Message;
|
statusMessage?: Message;
|
||||||
|
serverContext?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function answerQuestion({
|
export async function answerQuestion({
|
||||||
@@ -27,6 +28,7 @@ export async function answerQuestion({
|
|||||||
userId,
|
userId,
|
||||||
messageHistory,
|
messageHistory,
|
||||||
statusMessage,
|
statusMessage,
|
||||||
|
serverContext,
|
||||||
}: AnswerQuestionOptions): Promise<void> {
|
}: AnswerQuestionOptions): Promise<void> {
|
||||||
try {
|
try {
|
||||||
let prompt = question;
|
let prompt = question;
|
||||||
@@ -42,10 +44,11 @@ export async function answerQuestion({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tracker = new ToolTracker();
|
const tracker = new ToolTracker();
|
||||||
|
const systemPrompt = buildSystemPrompt(serverContext);
|
||||||
|
|
||||||
const result = streamText({
|
const result = streamText({
|
||||||
model,
|
model,
|
||||||
system: SYSTEM_PROMPT,
|
system: systemPrompt,
|
||||||
prompt,
|
prompt,
|
||||||
tools: aiTools,
|
tools: aiTools,
|
||||||
stopWhen: stepCountIs(MAX_STEPS),
|
stopWhen: stepCountIs(MAX_STEPS),
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import dedent from "dedent";
|
||||||
|
|
||||||
export const MAX_STEPS = 10;
|
export const MAX_STEPS = 10;
|
||||||
export const SYSTEM_PROMPT = `You are a helpful assistant in the goose Discord server.
|
|
||||||
|
export function buildSystemPrompt(serverContext?: string): string {
|
||||||
|
let prompt = dedent`You are a helpful assistant in the goose Discord server.
|
||||||
Your role is to provide assistance and answer questions about codename goose, an open-source AI agent developed by Block. codename goose's website is \`https://block.github.io/goose\`. Your answers should be short and to the point. Always assume that a user's question is related to codename goose unless they specifically state otherwise. DO NOT capitalize "goose" or "codename goose".
|
Your role is to provide assistance and answer questions about codename goose, an open-source AI agent developed by Block. codename goose's website is \`https://block.github.io/goose\`. Your answers should be short and to the point. Always assume that a user's question is related to codename goose unless they specifically state otherwise. DO NOT capitalize "goose" or "codename goose".
|
||||||
|
|
||||||
You can perform a maximum of ${MAX_STEPS} steps (tool calls, text outputs, etc.). If you exceed this limit, no response will be provided to the user. BEFORE you reach the limit, STOP calling tools, respond to the user, and don't call any tools after your final response until the user asks another question.
|
You can perform a maximum of ${MAX_STEPS} steps (tool calls, text outputs, etc.). If you exceed this limit, no response will be provided to the user. BEFORE you reach the limit, STOP calling tools, respond to the user, and don't call any tools after your final response until the user asks another question.
|
||||||
@@ -11,3 +15,10 @@ When answering questions about goose:
|
|||||||
4. Cite the documentation source in your response (using its Web URL)
|
4. Cite the documentation source in your response (using its Web URL)
|
||||||
|
|
||||||
When providing links, wrap the URL in angle brackets (e.g., \`<https://example.com>\` or \`[Example](<https://example.com>)\`) to prevent excessive link previews. Do not use backtick characters around the URL.`;
|
When providing links, wrap the URL in angle brackets (e.g., \`<https://example.com>\` or \`[Example](<https://example.com>)\`) to prevent excessive link previews. Do not use backtick characters around the URL.`;
|
||||||
|
|
||||||
|
if (serverContext) {
|
||||||
|
prompt += `\n\n${serverContext}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import type { Guild, TextChannel } from "discord.js";
|
||||||
|
import { ChannelType } from "discord.js";
|
||||||
|
|
||||||
|
export async function buildServerContext(guild: Guild): Promise<string> {
|
||||||
|
try {
|
||||||
|
const channels = await guild.channels.fetch();
|
||||||
|
|
||||||
|
const textChannels = Array.from(channels.values())
|
||||||
|
.filter(
|
||||||
|
(ch): ch is TextChannel =>
|
||||||
|
ch?.type === ChannelType.GuildText && ch !== null,
|
||||||
|
)
|
||||||
|
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
|
||||||
|
|
||||||
|
if (textChannels.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelList = textChannels
|
||||||
|
.map(
|
||||||
|
(ch) =>
|
||||||
|
`- ID: ${ch.id}; Name: ${ch.name}; ${ch.topic ? `Topic: ${ch.topic}` : ""}`,
|
||||||
|
)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return `## Server Channels
|
||||||
|
If a user asks about the server's channels or where to find something, here's the current channel list:
|
||||||
|
${channelList}
|
||||||
|
|
||||||
|
When mentioning a channel, provide the link to the channel rather than using the plain text name. You can link to a channel by using the following format: \`<#channelId>\`.`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error building server context:", error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user