Skip to content

Commit

Permalink
[JS] chore: Update samples with actions to tools augmentation (#1966)
Browse files Browse the repository at this point in the history
## Linked issues

closes: #1522

## Attestation Checklist

- [x] My code follows the style guidelines of this project

- I have checked for/fixed spelling, linting, and other errors
- I have commented my code for clarity
- I have made corresponding changes to the documentation (updating the
doc strings in the code is sufficient)
- My changes generate no new warnings
- I have added tests that validates my changes, and provides sufficient
test coverage. I have tested with:
  - Local testing
  - E2E testing in Teams
- New and existing unit tests pass locally with my changes

---------

Co-authored-by: Corina Gum <>
  • Loading branch information
corinagum authored Sep 3, 2024
1 parent a8bcc64 commit a1b1e40
Show file tree
Hide file tree
Showing 18 changed files with 490 additions and 74 deletions.
8 changes: 4 additions & 4 deletions js/samples/03.ai-concepts/a.twentyQuestions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import * as restify from 'restify';
import { ActivityTypes, ConfigurationServiceClientCredentialFactory, MemoryStorage, TurnContext } from 'botbuilder';

import {
ApplicationBuilder,
ActionPlanner,
ApplicationBuilder,
OpenAIModel,
PromptManager,
TurnState,
TeamsAdapter
TeamsAdapter,
TurnState
} from '@microsoft/teams-ai';

import * as responses from './responses';
Expand Down Expand Up @@ -110,7 +110,7 @@ const prompts = new PromptManager({
const planner = new ActionPlanner({
model,
prompts,
defaultPrompt: 'monologue'
defaultPrompt: 'hint'
});

// Define storage and application
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const prompts = new PromptManager({
const planner = new ActionPlanner({
model,
prompts,
defaultPrompt: 'monologue'
defaultPrompt: 'default'
});

// Define storage and application
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"stop_sequences": []
},
"augmentation": {
"augmentation_type": "monologue"
"augmentation_type": "tools"
}
}
95 changes: 72 additions & 23 deletions js/samples/03.ai-concepts/e.customModel-LLAMA/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@ import * as restify from 'restify';

// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
import { ConfigurationServiceClientCredentialFactory, MemoryStorage, TurnContext } from 'botbuilder';
import { ActivityTypes, ConfigurationServiceClientCredentialFactory, MemoryStorage, TurnContext } from 'botbuilder';

import {
ActionPlanner,
Application,
ApplicationBuilder,
LlamaModel,
Memory,
PromptManager,
TeamsAdapter,
TurnState
} from '@microsoft/teams-ai';

import * as responses from './responses';

// Read botFilePath and botFileSecret from .env file.
const ENV_FILE = path.join(__dirname, '..', '.env');
config({ path: ENV_FILE });
Expand Down Expand Up @@ -69,8 +70,11 @@ server.listen(process.env.port || process.env.PORT || 3978, () => {
console.log('\nTo test your bot in Teams, sideload the app manifest.json within Teams Apps.');
});

// Strongly type the applications turn state
interface ConversationState {
lightsOn: boolean;
secretWord: string;
guessCount: number;
remainingGuesses: number;
}
type ApplicationTurnState = TurnState<ConversationState>;

Expand All @@ -97,29 +101,56 @@ const planner = new ActionPlanner({
// Define storage and application
// - Note that we're not passing a prompt for our AI options as we won't be chatting with the app.
const storage = new MemoryStorage();
const app = new Application<ApplicationTurnState>({
storage,
ai: {
planner
}
});
const app = new ApplicationBuilder<ApplicationTurnState>().withStorage(storage).build();

// Define a prompt function for getting the current status of the lights
planner.prompts.addFunction('getLightStatus', async (context: TurnContext, memory: Memory) => {
return memory.getValue('conversation.lightsOn') ? 'on' : 'off';
// List for /reset command and then delete the conversation state
app.message('/quit', async (context: TurnContext, state: ApplicationTurnState) => {
const { secretWord } = state.conversation;
state.deleteConversationState();
await context.sendActivity(responses.quitGame(secretWord));
});

// Register action handlers
app.ai.action('LightsOn', async (context: TurnContext, state: ApplicationTurnState) => {
state.conversation.lightsOn = true;
await context.sendActivity(`[lights on]`);
return `the lights are now on`;
});
app.activity(ActivityTypes.Message, async (context: TurnContext, state: ApplicationTurnState) => {
let { secretWord, guessCount, remainingGuesses } = state.conversation;
if (secretWord && secretWord.length < 1) {
throw new Error('No secret word is assigned.');
}
if (secretWord) {
guessCount++;
remainingGuesses--;

// Check for correct guess
if (context.activity.text.toLowerCase().indexOf(secretWord.toLowerCase()) >= 0) {
await context.sendActivity(responses.youWin(secretWord));
secretWord = '';
guessCount = remainingGuesses = 0;
} else if (remainingGuesses == 0) {
await context.sendActivity(responses.youLose(secretWord));
secretWord = '';
guessCount = remainingGuesses = 0;
} else {
// Ask GPT for a hint
const response = await getHint(context, state);
if (response.toLowerCase().indexOf(secretWord.toLowerCase()) >= 0) {
await context.sendActivity(`[${guessCount}] ${responses.blockSecretWord()}`);
} else if (remainingGuesses == 1) {
await context.sendActivity(`[${guessCount}] ${responses.lastGuess(response)}`);
} else {
await context.sendActivity(`[${guessCount}] ${response}`);
}
}
} else {
// Start new game
secretWord = responses.pickSecretWord();
guessCount = 0;
remainingGuesses = 20;
await context.sendActivity(responses.startGame());
}

app.ai.action('LightsOff', async (context: TurnContext, state: ApplicationTurnState) => {
state.conversation.lightsOn = false;
await context.sendActivity(`[lights off]`);
return `the lights are now off`;
// Save game state
state.conversation.secretWord = secretWord;
state.conversation.guessCount = guessCount;
state.conversation.remainingGuesses = remainingGuesses;
});

// Listen for incoming server requests.
Expand All @@ -130,3 +161,21 @@ server.post('/api/messages', async (req, res) => {
await app.run(context);
});
});

/**
* Generates a hint for the user based on their input.
* @param {TurnContext} context The current turn context.
* @param {ApplicationTurnState} state The current turn state.
* @returns {Promise<string>} A promise that resolves to a string containing the generated hint.
* @throws {Error} If the request to LLM was rate limited.
*/
async function getHint(context: TurnContext, state: ApplicationTurnState): Promise<string> {
state.temp.input = context.activity.text;
const result = await planner.completePrompt(context, state, 'hint');

if (result.status !== 'success') {
throw result.error!;
}

return result.message!.content!;
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"schema": 1.1,
"description": "A bot that plays a game of 20 questions",
"type": "completion",
"completion": {
"completion_type": "chat",
"include_history": false,
"include_input": true,
"max_input_tokens": 2000,
"max_tokens": 256,
"temperature": 0.7,
"top_p": 0.0,
"presence_penalty": 0.6,
"frequency_penalty": 0.0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
You are the AI in a game of 20 questions.
The goal of the game is for the Human to guess a secret within 20 questions.
The AI should answer questions about the secret.
The AI should assume that every message from the Human is a question about the secret.

GuessCount: {{$conversation.guessCount}}
RemainingGuesses: {{$conversation.remainingGuesses}}
Secret: {{$conversation.secretWord}}

Answer the humans question but do not mention the secret word.
Loading

0 comments on commit a1b1e40

Please sign in to comment.