-
Notifications
You must be signed in to change notification settings - Fork 21
feat: showcase thinking steps #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
58a717b
e8bca5e
25d7fef
9bfcde4
378de85
4e2fadc
fd45e19
23ac1e3
7b4b845
991f182
8a40c6d
913c824
5eef6c3
fece0bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
srtaalej marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| import { OpenAI } from 'openai'; | ||
| import { rollDice, rollDiceDefinition } from './tools/dice.js'; | ||
|
|
||
| // OpenAI LLM client | ||
| const openai = new OpenAI({ | ||
| apiKey: process.env.OPENAI_API_KEY, | ||
| }); | ||
|
Comment on lines
+4
to
+7
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌚 question: Is the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch! it is not |
||
|
|
||
| /** | ||
| * Stream an LLM response to prompts with an example dice rolling function | ||
| * | ||
| * @param {import("@slack/web-api").ChatStreamer} streamer - Slack chat stream | ||
|
Check failure on line 12 in agent/llm-caller.js
|
||
|
srtaalej marked this conversation as resolved.
|
||
| * @param {Array} prompts - OpenAI ResponseInputParam messages | ||
| * | ||
| * @see {@link https://docs.slack.dev/tools/bolt-js/web#sending-streaming-messages} | ||
| * @see {@link https://platform.openai.com/docs/guides/text} | ||
| * @see {@link https://platform.openai.com/docs/guides/streaming-responses} | ||
| * @see {@link https://platform.openai.com/docs/guides/function-calling} | ||
| */ | ||
| export async function callLLM(streamer, prompts) { | ||
| const toolCalls = []; | ||
|
|
||
| const response = await openai.responses.create({ | ||
| model: 'gpt-4o-mini', | ||
| input: prompts, | ||
| tools: [rollDiceDefinition], | ||
| tool_choice: 'auto', | ||
| stream: true, | ||
| }); | ||
|
|
||
| for await (const event of response) { | ||
| // Stream markdown text from the LLM response as it arrives | ||
| if (event.type === 'response.output_text.delta' && event.delta) { | ||
| await streamer.append({ | ||
| markdown_text: event.delta, | ||
| }); | ||
| } | ||
|
|
||
| // Save function calls for later computation and a new task is shown | ||
| if (event.type === 'response.output_item.done') { | ||
| if (event.item.type === 'function_call') { | ||
| toolCalls.push(event.item); | ||
|
|
||
| if (event.item.name === 'roll_dice') { | ||
| const args = JSON.parse(event.item.arguments); | ||
| await streamer.append({ | ||
| chunks: [ | ||
| { | ||
| type: 'task_update', | ||
| id: event.item.call_id, | ||
| title: `Rolling a ${args.count}d${args.sides}...`, | ||
| status: 'in_progress', | ||
| }, | ||
| ], | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Perform tool calls and marks tasks as completed | ||
| if (toolCalls.length > 0) { | ||
| for (const call of toolCalls) { | ||
| if (call.name === 'roll_dice') { | ||
| const args = JSON.parse(call.arguments); | ||
|
|
||
| prompts.push({ | ||
| id: call.id, | ||
| call_id: call.call_id, | ||
| type: 'function_call', | ||
| name: 'roll_dice', | ||
| arguments: call.arguments, | ||
| }); | ||
|
|
||
| const result = rollDice(args); | ||
|
|
||
| prompts.push({ | ||
| type: 'function_call_output', | ||
| call_id: call.call_id, | ||
| output: JSON.stringify(result), | ||
| }); | ||
|
|
||
| if (result.error != null) { | ||
| await streamer.append({ | ||
| chunks: [ | ||
| { | ||
| type: 'task_update', | ||
| id: call.call_id, | ||
| title: result.error, | ||
| status: 'error', | ||
| }, | ||
| ], | ||
| }); | ||
| } else { | ||
| await streamer.append({ | ||
| chunks: [ | ||
| { | ||
| type: 'task_update', | ||
| id: call.call_id, | ||
| title: result.description, | ||
| status: 'complete', | ||
| }, | ||
| ], | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // complete the llm response after making tool calls | ||
| await callLLM(streamer, prompts); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /** | ||
| * Roll one or more dice with a specified number of sides. | ||
| * | ||
| * @param {Object} options - The roll options | ||
| * @param {number} [options.sides=6] - The number of sides on the die | ||
| * @param {number} [options.count=1] - The number of dice to roll | ||
| * @returns {Object} The roll results with rolls array, total, and description | ||
| */ | ||
| export function rollDice({ sides = 6, count = 1 } = {}) { | ||
| if (sides < 2) { | ||
| return { | ||
| error: 'A die must have at least 2 sides', | ||
| rolls: [], | ||
| total: 0, | ||
| }; | ||
| } | ||
|
|
||
| if (count < 1) { | ||
| return { | ||
| error: 'Must roll at least 1 die', | ||
| rolls: [], | ||
| total: 0, | ||
| }; | ||
| } | ||
|
|
||
| const rolls = Array.from({ length: count }, () => Math.floor(Math.random() * sides) + 1); | ||
| const total = rolls.reduce((sum, roll) => sum + roll, 0); | ||
|
|
||
| return { | ||
| rolls, | ||
| total, | ||
| description: `Rolled a ${count}d${sides} to total ${total}`, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Tool definition for OpenAI API | ||
| * | ||
| * @type {import('openai/resources/responses/responses').Tool} | ||
| * @see {@link https://platform.openai.com/docs/guides/function-calling} | ||
| */ | ||
| export const rollDiceDefinition = { | ||
|
Comment on lines
+36
to
+42
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🪬 note: Using |
||
| type: 'function', | ||
| name: 'roll_dice', | ||
| description: | ||
| 'Roll one or more dice with a specified number of sides. Use this when the user wants to roll dice or generate random numbers within a range.', | ||
| parameters: { | ||
| type: 'object', | ||
| properties: { | ||
| sides: { | ||
| type: 'integer', | ||
| description: 'The number of sides on the die (e.g., 6 for a standard die, 20 for a d20)', | ||
| default: 6, | ||
| }, | ||
| count: { | ||
| type: 'integer', | ||
| description: 'The number of dice to roll', | ||
| default: 1, | ||
| }, | ||
| }, | ||
| required: ['sides', 'count'], | ||
| }, | ||
| strict: false, | ||
| }; | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note - still deciding how to restructure README.md to make setup easier. I think the most obvious point of confusion is deciding whether to use CLI or terminal to setup, i think a toggle like
Details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@srtaalej Thanks for calling this out! 📚 ✨ I agree it's a confusing and hope we can encourage the CLI most-
IIRC @lukegalbraithrussell had thoughts on related changes as well and we should match such in slack-samples/bolt-python-assistant-template#42 too!