├── .DS_Store
├── .env
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
├── launch.json
└── tasks.json
├── LICENSE
├── README.md
├── appPackage
├── JARVIB_192.png
├── JARVIB_32.png
├── JARVIB_512.jpg
├── color.png
├── manifest.json
└── outline.png
├── env
├── .env.local
├── .env.local.user
└── .env.staging
├── images
├── F5launch001.png
├── F5launch002.png
├── F5launch003.jpg
├── F5launch004.png
├── F5launch005.png
├── F5launch006.png
├── F5launch007.jpg
├── F5launch008.jpg
└── F5launch009.jpg
├── infra
├── azure.bicep
├── azure.parameters.json
└── botRegistration
│ └── azurebot.bicep
├── package-lock.json
├── package.json
├── promptsExamples.txt
├── src
├── .DS_Store
├── app.js
├── debug.html
├── index.html
├── index.ts
├── prompts
│ ├── .DS_Store
│ ├── chatGPT
│ │ ├── actions.json
│ │ ├── config.json
│ │ └── skprompt.txt
│ └── describe
│ │ ├── config.json
│ │ └── skprompt.txt
└── responses.ts
├── teamsapp.local.yml
├── teamsapp.yml
├── tsconfig.json
├── web.config
└── yarn.lock
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/.DS_Store
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | BOT_ID=
2 | BOT_PASSWORD=
3 | OPENAI_KEY=
4 | AZURE_OPENAI_KEY=
5 | AZURE_OPENAI_ENDPOINT=
6 | AZURE_OPENAI_API_MODEL=
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | bin
2 | build
3 | demo-packages
4 | dist
5 | manifest
6 | node_modules
7 | package-lock.json
8 | docs/assets/main.js
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "root": true,
4 | "env": {
5 | "browser": true,
6 | "node": true,
7 | "es2015": true,
8 | "mocha": true,
9 | "jest": true
10 | },
11 | "extends": [
12 | "eslint:recommended",
13 | "plugin:@typescript-eslint/recommended",
14 | "plugin:import/typescript",
15 | "plugin:import/recommended",
16 | "plugin:jsdoc/recommended",
17 |
18 | "plugin:security/recommended",
19 | "plugin:prettier/recommended" // Recommended to be last
20 | ],
21 | "plugins": [
22 | "@typescript-eslint",
23 | "jsdoc",
24 |
25 | "mocha",
26 | "only-warn",
27 | "prettier"
28 | // "react"
29 | ],
30 | "parserOptions": {
31 | "ecmaVersion": 2015,
32 | // Allows for the parsing of modern ECMAScript features
33 | "sourceType": "module" // Allows for the use of imports
34 | // "ecmaFeatures": {
35 | // "jsx": true
36 | // }
37 | },
38 | "rules": {
39 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
40 | "@typescript-eslint/ban-types": "off",
41 | "@typescript-eslint/explicit-function-return-type": "off",
42 | "@typescript-eslint/explicit-member-accessibility": "off",
43 | "@typescript-eslint/explicit-module-boundary-types": "off",
44 | "@typescript-eslint/interface-name-prefix": "off",
45 | "@typescript-eslint/no-empty-function": "off",
46 | "@typescript-eslint/no-explicit-any": "off",
47 | "@typescript-eslint/no-namespace": "off",
48 | "@typescript-eslint/no-unused-vars": "off",
49 | "@typescript-eslint/no-non-null-assertion": "off",
50 | "no-async-promise-executor": "off",
51 | "no-constant-condition": "off",
52 | "no-undef": "off", // Disabled due to conflicts with @typescript/eslint
53 | "no-unused-vars": "off", // Disabled due to conflicts with @typescript/eslint
54 | "prettier/prettier": "error"
55 | },
56 | "overrides": [
57 | {
58 | "files": ["bin/*.js", "lib/*.js"]
59 | }
60 | ],
61 | "ignorePatterns": ["node_modules/*"],
62 | "settings": {
63 | // "react": {
64 | // "version": "detect"
65 | // }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .zip
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | lerna-debug.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 | /**/lib
23 | lib
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 | *.lcov
28 |
29 | # nyc test coverage
30 | .nyc_output
31 |
32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33 | .grunt
34 |
35 | # Bower dependency directory (https://bower.io/)
36 | bower_components
37 |
38 | # node-waf configuration
39 | .lock-wscript
40 |
41 | # Compiled binary addons (https://nodejs.org/api/addons.html)
42 | build/Release
43 |
44 | # Dependency directories
45 | node_modules/
46 | jspm_packages/
47 |
48 | # TypeScript v1 declaration files
49 | typings/
50 |
51 | # TypeScript cache
52 | *.tsbuildinfo
53 |
54 | # Optional npm cache directory
55 | .npm
56 |
57 | # Optional eslint cache
58 | .eslintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # Teams Toolkit
107 | appPackage/build
108 | .deployment
109 |
110 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | bin
2 | build
3 | demo-packages
4 | dist
5 | manifest
6 | node_modules
7 | package-lock.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "endOfLine": "auto",
4 | "printWidth": 120,
5 | "semi": true,
6 | "singleAttributePerLine": false,
7 | "singleQuote": true,
8 | "tabWidth": 4,
9 | "trailingComma": "none",
10 | "useTabs": false
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Remote (Edge)",
6 | "type": "msedge",
7 | "request": "launch",
8 | "url": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
9 | "presentation": {
10 | "group": "remote",
11 | "order": 1
12 | },
13 | "internalConsoleOptions": "neverOpen"
14 | },
15 | {
16 | "name": "Launch Remote (Chrome)",
17 | "type": "chrome",
18 | "request": "launch",
19 | "url": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
20 | "presentation": {
21 | "group": "remote",
22 | "order": 2
23 | },
24 | "internalConsoleOptions": "neverOpen"
25 | },
26 | {
27 | "name": "Launch App (Edge)",
28 | "type": "msedge",
29 | "request": "launch",
30 | "url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
31 | "cascadeTerminateToConfigurations": [
32 | "Attach to Local Service"
33 | ],
34 | "presentation": {
35 | "group": "all",
36 | "hidden": true
37 | },
38 | "internalConsoleOptions": "neverOpen"
39 | },
40 | {
41 | "name": "Launch App (Chrome)",
42 | "type": "chrome",
43 | "request": "launch",
44 | "url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
45 | "cascadeTerminateToConfigurations": [
46 | "Attach to Local Service"
47 | ],
48 | "presentation": {
49 | "group": "all",
50 | "hidden": true
51 | },
52 | "internalConsoleOptions": "neverOpen"
53 | },
54 | {
55 | "name": "Attach to Local Service",
56 | "type": "node",
57 | "request": "attach",
58 | "port": 9239,
59 | "restart": true,
60 | "presentation": {
61 | "group": "all",
62 | "hidden": true
63 | },
64 | "internalConsoleOptions": "neverOpen"
65 | }
66 | ],
67 | "compounds": [
68 | {
69 | "name": "Debug (Edge)",
70 | "configurations": [
71 | "Launch App (Edge)",
72 | "Attach to Local Service"
73 | ],
74 | "preLaunchTask": "Start Teams App Locally",
75 | "presentation": {
76 | "group": "all",
77 | "order": 1
78 | },
79 | "stopAll": true
80 | },
81 | {
82 | "name": "Debug (Chrome)",
83 | "configurations": [
84 | "Launch App (Chrome)",
85 | "Attach to Local Service"
86 | ],
87 | "preLaunchTask": "Start Teams App Locally",
88 | "presentation": {
89 | "group": "all",
90 | "order": 2
91 | },
92 | "stopAll": true
93 | }
94 | ]
95 | }
96 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // This file is automatically generated by Teams Toolkit.
2 | // The teamsfx tasks defined in this file require Teams Toolkit version >= 5.0.0.
3 | // See https://aka.ms/teamsfx-tasks for details on how to customize each task.
4 | {
5 | "version": "2.0.0",
6 | "tasks": [
7 | {
8 | "label": "Start Teams App Locally",
9 | "dependsOn": [
10 | "Validate prerequisites",
11 | "Start local tunnel",
12 | "Provision",
13 | "Deploy",
14 | "Start application"
15 | ],
16 | "dependsOrder": "sequence"
17 | },
18 | {
19 | // Check all required prerequisites.
20 | // See https://aka.ms/teamsfx-tasks/check-prerequisites to know the details and how to customize the args.
21 | "label": "Validate prerequisites",
22 | "type": "teamsfx",
23 | "command": "debug-check-prerequisites",
24 | "args": {
25 | "prerequisites": [
26 | "nodejs", // Validate if Node.js is installed.
27 | "m365Account", // Sign-in prompt for Microsoft 365 account, then validate if the account enables the sideloading permission.
28 | "portOccupancy" // Validate available ports to ensure those debug ones are not occupied.
29 | ],
30 | "portOccupancy": [
31 | 3978, // app service port
32 | 9239, // app inspector port for Node.js debugger
33 | 3000 // use for WebSocket connection
34 | ]
35 | }
36 | },
37 | {
38 | // Start the local tunnel service to forward public URL to local port and inspect traffic.
39 | // See https://aka.ms/teamsfx-tasks/local-tunnel for the detailed args definitions.
40 | "label": "Start local tunnel",
41 | "type": "teamsfx",
42 | "command": "debug-start-local-tunnel",
43 | "args": {
44 | "type": "dev-tunnel",
45 | "ports": [
46 | {
47 | "portNumber": 3978,
48 | "protocol": "http",
49 | "access": "public",
50 | "writeToEnvironmentFile": {
51 | "endpoint": "BOT_ENDPOINT", // output tunnel endpoint as BOT_ENDPOINT
52 | "domain": "BOT_DOMAIN" // output tunnel domain as BOT_DOMAIN
53 | }
54 | },
55 | {
56 | "portNumber": 3000,
57 | "protocol": "http",
58 | "access": "public",
59 | "writeToEnvironmentFile": {
60 | "endpoint": "TAB_ENDPOINT",
61 | "domain": "TAB_DOMAIN"
62 | }
63 | }
64 | ],
65 | "env": "local"
66 | },
67 | "isBackground": true,
68 | "problemMatcher": "$teamsfx-local-tunnel-watch"
69 | },
70 | {
71 | // Create the debug resources.
72 | // See https://aka.ms/teamsfx-tasks/provision to know the details and how to customize the args.
73 | "label": "Provision",
74 | "type": "teamsfx",
75 | "command": "provision",
76 | "args": {
77 | "env": "local"
78 | }
79 | },
80 | {
81 | // Build project.
82 | // See https://aka.ms/teamsfx-tasks/deploy to know the details and how to customize the args.
83 | "label": "Deploy",
84 | "type": "teamsfx",
85 | "command": "deploy",
86 | "args": {
87 | "env": "local"
88 | }
89 | },
90 | {
91 | "label": "Start application",
92 | "type": "shell",
93 | "command": "npm run dev:teamsfx",
94 | "isBackground": true,
95 | "options": {
96 | "cwd": "${workspaceFolder}"
97 | },
98 | "problemMatcher": {
99 | "pattern": [
100 | {
101 | "regexp": "^.*$",
102 | "file": 0,
103 | "location": 1,
104 | "message": 2
105 | }
106 | ],
107 | "background": {
108 | "activeOnStart": true,
109 | "beginsPattern": "[nodemon] starting",
110 | "endsPattern": "restify listening to|Bot/ME service listening at|[nodemon] app crashed"
111 | }
112 | }
113 | }
114 | ]
115 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 David Rousset
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AI in Microsoft Teams: J.A.R.V.I.B.
2 |
3 | *Your custom Microsoft Teams AI Copilot to build interactive dynamic 3D worlds*
4 |
5 |
6 |
7 | JARVIB (Just A Rather Very Intelligent Bot) is your personnal Copilot to help you designing 3D worlds in a collaborative way inside a personnal conversation or in a Teams meeting. Ask him to create various Babylon.js primitive or to search for specific models to load them into the scene. You can also ask to the AI to visually describe the scene for accessibility reasons. Look at possible prompts in ```promptsExamples.txt```
8 |
9 |
10 |
11 |
12 |
13 | - [AI in Microsoft Teams: J.A.R.V.I.B.](#ai-in-microsoft-teams-list-bot)
14 | - [View it in action](#view-it-in-action)
15 | - [Setting up the sample](#setting-up-the-sample)
16 | - [Interacting with the bot](#interacting-with-the-bot)
17 | - [Further reading](#further-reading)
18 |
19 |
20 |
21 | ## View it in action
22 |
23 | [](https://www.youtube.com/watch?v=181D9lk7DRc)
24 |
25 | JARVIB is using the Teams AI library to map users' intent to 3 different type of actions:
26 |
27 | - **codeToExecute** which will take the Babylon.js code generated by the LLM and send it to the web page containing the 3D canvas to execute it via WebSocket
28 | - **listAvailableModel** which will use the same API as PowerPoint to find for existing 3D models and return a list of available models to load through an Adaptive Card list.
29 | - **loadThisModel** which will load one of the models exposed in the list displayed before
30 |
31 | You have also 3 commands available:
32 |
33 | - **/reset** will clear the conversation state, clean the various objects states and reload the web page containing the 3D canvas to start from scratch
34 | - **/describe** will ask to the AI, via the text-davinci-003 LLM model to visually describe the scene
35 | - **/fullcode** will return all Babylon.js code executed so far so you can copy/paste it in your project or in the Babylon.js Playground
36 |
37 | Look at the ```/src/prompts``` in ```/ChatGPT/skprompt.txt``` to check how the AI is prompted and check the ```/ChatGPT/actions.json``` file to understand how the various actions are described for the 3 Teams AI actions.
38 |
39 | The prompt configuration:
40 |
41 | ```
42 | Pretend you're an expert in Babylon.js, the JavaScript WebGL 3D engine.
43 |
44 | Assume there is already an existing Babylon.js scene and engine so you don't have to create them, just generate the code to add into an existing program.
45 | Use the scene and engine objects directly.
46 |
47 | Pay attention when trying to access previously created Meshes by getting access to them via their name rather than assuming the associated variable is already created.
48 | When writing a new code, consider all the previous one you've generated to be sure the new code will be consistent with the previous one.
49 | Remember about the already created meshes, animations or any other specific ressources before trying to create them or reuse them.
50 | ```
51 |
52 | The JSON schema to help the LLM to map to the defined actions:
53 |
54 | ```javascript
55 | [
56 | {
57 | "name": "codeToExecute",
58 | "description": "Returns the Babylon.js JavaScript code matching the user intent, building the next code working in a consistent way with the previous code already executed",
59 | "parameters": {
60 | "type": "object",
61 | "properties": {
62 | "code": {
63 | "type": "string",
64 | "description": "The JavaScript code to execute next"
65 | }
66 | },
67 | "required": [
68 | "code"
69 | ]
70 | }
71 | },
72 | {
73 | "name": "listAvailableModel",
74 | "description": "List the available 3D models we can load from the library",
75 | "parameters": {
76 | "type": "object",
77 | "properties": {
78 | "nameOfTheModel": {
79 | "type": "string",
80 | "description": "The name of the model to search inside the library"
81 | }
82 | },
83 | "required": [
84 | "nameOfTheModel"
85 | ]
86 | }
87 | },
88 | {
89 | "name": "loadThisModel",
90 | "description": "Load the 3D model specified by the user",
91 | "parameters": {
92 | "type": "object",
93 | "properties": {
94 | "nameOfTheModel": {
95 | "type": "string",
96 | "description": "The name of the model to load from the library"
97 | }
98 | },
99 | "required": [
100 | "nameOfTheModel"
101 | ]
102 | }
103 | }
104 | ]
105 | ```
106 |
107 | ## Setting up the sample
108 |
109 | 1. Clone the repository
110 |
111 | ```bash
112 | git clone https://github.com/davrous/JARVIB.git
113 | ```
114 |
115 | 2. Install the Teams Toolkit: https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/install-teams-toolkit
116 |
117 | 3. Set your Azure OpenAI key, endpoints and model names or your OpenAI key in the .env file.
118 |
119 | For Azure OpenAI, pay attention to use the "*Deployment name*" and not the "*Model name*" as the value for the properties in the .env file. If you've got an error 404 during execution, it's because you either entered the wrong endpoint or the wrong deployment name.
120 |
121 | 4. Press F5!
122 |
123 | The Team Toolkit will do a lot of magic for you by:
124 | - installing all dependencies and building the TypeScript code
125 | - registering the bot in the Azure Active Directory
126 | - building the Teams manifest file, zipping the resources to upload them in the Teams Developer Portal
127 | - creating 2 tunnels to expose your localhost on public URLs using VS Dev Tunnels, you can find the details in ```/.vscode/tasks.json```
128 | - 1 for the bot itself mapped on port 3978
129 | - 1 for the tab to be used inside a Teams meeting mapped on port 3000
130 | - launching the Teams web app to let you install the bot / meeting extension
131 |
132 | 5. If everything goes well, you should first be able that the 2 tunnels have been created:
133 |
134 |
135 |
136 | 6. Then, the webserver will be launched and you'll be asked to install JARVIB in your developer tenant:
137 |
138 |
139 |
140 | You can either simply press the "*Add*" button and this will install the app to let you discuss with it directly as a classical bot.
141 |
142 | You can also add it to an existing meeting to be able to replicate the full experience showcased in the video.
143 |
144 | ## Interacting with the bot
145 |
146 | The easiest way to try it is to install it using the "*Add*" button as a bot, then open http://localhost:3000/debug.html on the side open a simple canvas 3D websocket client. Then ask to the bot to create 3D content, load some models and so on:
147 |
148 |
149 |
150 | The other option to replicate the full experience is to load the app inside an existing meeting:
151 |
152 |
153 |
154 | You need to accept connecting to the VS Dev Tunnel redirecting to your localhost developer machine:
155 |
156 |
157 |
158 | Finally, save JARVIB as an app part of this chosen Teams meeting:
159 |
160 |
161 |
162 | Now, launch the Teams meeting alone or with someone else to collaborate with:
163 |
164 |
165 |
166 | Click on the JARVIB icon and then click on the "*Share to meeting*" button:
167 |
168 |
169 |
170 | Finally, call the AI bot via @JARVIB-local following your order:
171 |
172 |
173 |
174 | ## Further reading
175 |
176 | - [Teams AI library](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/teams%20conversational%20ai/teams-conversation-ai-overview)
177 | - [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview)
178 | - [Teams Toolkit overview](https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/teams-toolkit-fundamentals)
179 | - [How Microsoft Teams bots work](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-basics-teams?view=azure-bot-service-4.0&tabs=javascript)
180 |
--------------------------------------------------------------------------------
/appPackage/JARVIB_192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/appPackage/JARVIB_192.png
--------------------------------------------------------------------------------
/appPackage/JARVIB_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/appPackage/JARVIB_32.png
--------------------------------------------------------------------------------
/appPackage/JARVIB_512.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/appPackage/JARVIB_512.jpg
--------------------------------------------------------------------------------
/appPackage/color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/appPackage/color.png
--------------------------------------------------------------------------------
/appPackage/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
3 | "version": "1.0.0",
4 | "manifestVersion": "1.16",
5 | "id": "${{TEAMS_APP_ID}}",
6 | "packageName": "com.davrous.jarvib",
7 | "name": {
8 | "short": "JARVIB-${{TEAMSFX_ENV}}",
9 | "full": "Just A Rather Very Intelligent Bot that can create dynamic 3D worlds."
10 | },
11 | "developer": {
12 | "name": "David Rousset",
13 | "mpnId": "",
14 | "websiteUrl": "https://www.davrous.com",
15 | "privacyUrl": "https://www.davrous.com/",
16 | "termsOfUseUrl": "https://microsoft.com/termsofuse"
17 | },
18 | "description": {
19 | "short": "A smart bot that can create dynamic 3D worlds.",
20 | "full": "Just A Rather Very Intelligent Bot that can create dynamic 3D worlds."
21 | },
22 | "icons": {
23 | "outline": "JARVIB_32.png",
24 | "color": "JARVIB_192.png"
25 | },
26 | "accentColor": "#FFFFFF",
27 | "bots": [
28 | {
29 | "botId": "${{BOT_ID}}",
30 | "scopes": ["personal", "team", "groupChat"],
31 | "isNotificationOnly": false,
32 | "supportsCalling": false,
33 | "supportsVideo": false,
34 | "supportsFiles": false
35 | }
36 | ],
37 | "configurableTabs": [
38 | {
39 | "configurationUrl": "${{TAB_ENDPOINT}}/?view=config&inTeams=1&load=1",
40 | "canUpdateConfiguration": false,
41 | "scopes": [
42 | "groupchat"
43 | ],
44 | "context": [
45 | "meetingSidePanel",
46 | "meetingStage"
47 | ]
48 | }
49 | ],
50 | "permissions": [
51 | "identity",
52 | "messageTeamMembers"
53 | ],
54 | "validDomains": [
55 | "${{BOT_DOMAIN}}",
56 | "${{TAB_DOMAIN}}"
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------
/appPackage/outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/appPackage/outline.png
--------------------------------------------------------------------------------
/env/.env.local:
--------------------------------------------------------------------------------
1 | # This file includes environment variables that can be committed to git. It's gitignored by default because it represents your local development environment.
2 |
3 | # Built-in environment variables
4 | TEAMSFX_ENV=local
5 |
6 | # Generated during provision, you can also add your own variables.
7 | BOT_ID=
8 | TEAMS_APP_ID=
9 | BOT_DOMAIN=
10 | BOT_ENDPOINT=
11 | TEAMS_APP_TENANT_ID=
12 |
13 | # TO DO: Add your own environment variables values here.
14 | TAB_ENDPOINT=
15 | TAB_DOMAIN=
16 | APP_NAME_SUFFIX=local
--------------------------------------------------------------------------------
/env/.env.local.user:
--------------------------------------------------------------------------------
1 | SECRET_BOT_PASSWORD=
2 | TEAMS_APP_UPDATE_TIME=
--------------------------------------------------------------------------------
/env/.env.staging:
--------------------------------------------------------------------------------
1 | # This file includes environment variables that will be committed to git by default.
2 |
3 | # Built-in environment variables
4 | TEAMSFX_ENV=staging
5 |
6 | # Updating AZURE_SUBSCRIPTION_ID or AZURE_RESOURCE_GROUP_NAME after provision may also require an update to RESOURCE_SUFFIX, because some services require a globally unique name across subscriptions/resource groups.
7 | AZURE_SUBSCRIPTION_ID=
8 | AZURE_RESOURCE_GROUP_NAME=
9 | RESOURCE_SUFFIX=
10 |
11 | # Generated during provision, you can also add your own variables.
12 | BOT_ID=
13 | TEAMS_APP_ID=
14 | BOT_AZURE_APP_SERVICE_RESOURCE_ID=
15 | BOT_DOMAIN=
16 |
--------------------------------------------------------------------------------
/images/F5launch001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch001.png
--------------------------------------------------------------------------------
/images/F5launch002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch002.png
--------------------------------------------------------------------------------
/images/F5launch003.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch003.jpg
--------------------------------------------------------------------------------
/images/F5launch004.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch004.png
--------------------------------------------------------------------------------
/images/F5launch005.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch005.png
--------------------------------------------------------------------------------
/images/F5launch006.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch006.png
--------------------------------------------------------------------------------
/images/F5launch007.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch007.jpg
--------------------------------------------------------------------------------
/images/F5launch008.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch008.jpg
--------------------------------------------------------------------------------
/images/F5launch009.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/images/F5launch009.jpg
--------------------------------------------------------------------------------
/infra/azure.bicep:
--------------------------------------------------------------------------------
1 | @maxLength(20)
2 | @minLength(4)
3 | @description('Used to generate names for all resources in this file')
4 | param resourceBaseName string
5 |
6 | @description('Required when create Azure Bot service')
7 | param botAadAppClientId string
8 |
9 | @secure()
10 | @description('Required by Bot Framework package in your bot project')
11 | param botAadAppClientSecret string
12 |
13 | param webAppSKU string
14 |
15 | @maxLength(42)
16 | param botDisplayName string
17 |
18 | param serverfarmsName string = resourceBaseName
19 | param webAppName string = resourceBaseName
20 | param location string = resourceGroup().location
21 |
22 | // Compute resources for your Web App
23 | resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = {
24 | kind: 'app'
25 | location: location
26 | name: serverfarmsName
27 | sku: {
28 | name: webAppSKU
29 | }
30 | }
31 |
32 | // Web App that hosts your bot
33 | resource webApp 'Microsoft.Web/sites@2021-02-01' = {
34 | kind: 'app'
35 | location: location
36 | name: webAppName
37 | properties: {
38 | serverFarmId: serverfarm.id
39 | httpsOnly: true
40 | siteConfig: {
41 | alwaysOn: true
42 | appSettings: [
43 | {
44 | name: 'WEBSITE_RUN_FROM_PACKAGE'
45 | value: '1' // Run Azure APP Service from a package file
46 | }
47 | {
48 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
49 | value: '~16' // Set NodeJS version to 16.x for your site
50 | }
51 | {
52 | name: 'RUNNING_ON_AZURE'
53 | value: '1'
54 | }
55 | {
56 | name: 'BOT_ID'
57 | value: botAadAppClientId
58 | }
59 | {
60 | name: 'BOT_PASSWORD'
61 | value: botAadAppClientSecret
62 | }
63 | ]
64 | ftpsState: 'FtpsOnly'
65 | }
66 | }
67 | }
68 |
69 | // Register your web service as a bot with the Bot Framework
70 | module azureBotRegistration './botRegistration/azurebot.bicep' = {
71 | name: 'Azure-Bot-registration'
72 | params: {
73 | resourceBaseName: resourceBaseName
74 | botAadAppClientId: botAadAppClientId
75 | botAppDomain: webApp.properties.defaultHostName
76 | botDisplayName: botDisplayName
77 | }
78 | }
79 |
80 | // The output will be persisted in .env.{envName}. Visit https://aka.ms/teamsfx-actions/arm-deploy for more details.
81 | output BOT_AZURE_APP_SERVICE_RESOURCE_ID string = webApp.id
82 | output BOT_DOMAIN string = webApp.properties.defaultHostName
83 |
--------------------------------------------------------------------------------
/infra/azure.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "resourceBaseName": {
6 | "value": "bot${{RESOURCE_SUFFIX}}"
7 | },
8 | "botAadAppClientId": {
9 | "value": "${{BOT_ID}}"
10 | },
11 | "botAadAppClientSecret": {
12 | "value": "${{SECRET_BOT_PASSWORD}}"
13 | },
14 | "webAppSKU": {
15 | "value": "B1"
16 | },
17 | "botDisplayName": {
18 | "value": "ListBotInfra"
19 | },
20 | "openAIKey": {
21 | "value": "${{SECRET_OPENAI_KEY}}"
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/infra/botRegistration/azurebot.bicep:
--------------------------------------------------------------------------------
1 | @maxLength(20)
2 | @minLength(4)
3 | @description('Used to generate names for all resources in this file')
4 | param resourceBaseName string
5 |
6 | @maxLength(42)
7 | param botDisplayName string
8 |
9 | param botServiceName string = resourceBaseName
10 | param botServiceSku string = 'F0'
11 | param botAadAppClientId string
12 | param botAppDomain string
13 |
14 | // Register your web service as a bot with the Bot Framework
15 | resource botService 'Microsoft.BotService/botServices@2021-03-01' = {
16 | kind: 'azurebot'
17 | location: 'global'
18 | name: botServiceName
19 | properties: {
20 | displayName: botDisplayName
21 | endpoint: 'https://${botAppDomain}/api/messages'
22 | msaAppId: botAadAppClientId
23 | }
24 | sku: {
25 | name: botServiceSku
26 | }
27 | }
28 |
29 | // Connect the bot service to Microsoft Teams
30 | resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = {
31 | parent: botService
32 | location: 'global'
33 | name: 'MsTeamsChannel'
34 | properties: {
35 | channelName: 'MsTeamsChannel'
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JARVIB",
3 | "version": "1.0.0",
4 | "description": "Just A Rather Very Intelligent Bot that can create dynamic 3D worlds.",
5 | "author": "David Rousset",
6 | "license": "MIT",
7 | "main": "./lib/index.js",
8 | "scripts": {
9 | "build": "tsc --build && shx cp -r ./src/prompts ./lib/",
10 | "clean": "rimraf node_modules lib tsconfig.tsbuildinfo",
11 | "lint": "eslint **/src/**/*.{j,t}s{,x} --fix --no-error-on-unmatched-pattern",
12 | "start": "tsc --build && node ./lib/index.js",
13 | "test": "echo \"Error: no test specified\" && exit 1",
14 | "watch": "nodemon --watch ./src -e ts --exec \"yarn start\"",
15 | "dev:teamsfx": "nodemon --exec node --inspect=9239 --signal SIGINT -r ts-node/register ./src/index.ts"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/davrous/JARVIB.git"
20 | },
21 | "dependencies": {
22 | "@azure/msal-node": "^2.6.4",
23 | "@microsoft/teams-ai": "~1.1.0",
24 | "@microsoft/teams-js": "^2.14.0",
25 | "@microsoft/teamsfx": "^2.3.0",
26 | "botbuilder": "^4.22.1",
27 | "dotenv": "^16.4.2",
28 | "express": "^4.18.2",
29 | "replace": "~1.2.0",
30 | "restify": "~11.1.0",
31 | "rimraf": "^5.0.5",
32 | "socket.io": "^4.7.2"
33 | },
34 | "devDependencies": {
35 | "@types/dotenv": "6.1.1",
36 | "@types/jsonwebtoken": "^9.0.2",
37 | "@types/lodash": "^4.14.196",
38 | "@types/node": "^20.11.19",
39 | "@types/restify": "8.5.12",
40 | "nodemon": "~1.19.4",
41 | "shx": "^0.3.4",
42 | "ts-node": "^10.9.1",
43 | "typescript": "^5.1.6"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/promptsExamples.txt:
--------------------------------------------------------------------------------
1 | create 10 cubes of different colors and place them on a circle
2 |
3 | animate the rotation of the cube 5 and 9
4 |
5 | make the cube 3 transparent and move it up by 2 units
6 |
7 | find available models for a car
8 |
9 | load the purple one
10 |
11 | scale it to 80
12 |
13 | use the cube 4 position to move the purple car just on top of it
14 |
15 | load the C7 car
16 |
17 | scale it to 80
18 |
19 | attach it to the cube 5
20 |
21 | make the cube 5 totally transparent
22 |
23 | find models for a house
24 |
25 | load a scary one
26 |
27 | scale it to 10
28 |
29 | create a yellowish ground and move it down by 1 unit
30 |
31 | create an orange spot light
32 |
33 | animate its position like around a circle
34 |
35 | slow down the animation
36 |
37 | enable VR support
38 |
39 | animate the main scene active camera to circle around the haunted house
40 |
41 | slow down the animation by 10
42 |
43 |
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davrous/JARVIB/f3197db6ad9d6c9691f3c392d2ed9aeb87720d6f/src/.DS_Store
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License.
4 | */
5 |
6 | // Code used when the app is loaded in the Teams meeting
7 | // We're using the Teams JS SDK to get the context of our app in Teams
8 |
9 | const searchParams = new URL(window.location).searchParams;
10 | const root = document.getElementById("content");
11 | let color = "white";
12 |
13 | // STARTUP LOGIC
14 | async function start() {
15 | // Check for page to display
16 | let view = searchParams.get("view") || "stage";
17 |
18 | // Check if we are running on stage.
19 | if (searchParams.get("inTeams")) {
20 | // Initialize teams app
21 | await microsoftTeams.app.initialize();
22 |
23 | // Get our frameContext from context of our app in Teams
24 | const context = await microsoftTeams.app.getContext();
25 | if (context.page.frameContext == "meetingStage") {
26 | view = "stage";
27 | }
28 | const theme = context.app.theme;
29 | if (theme == "default") {
30 | color = "black";
31 | }
32 | microsoftTeams.app.registerOnThemeChangeHandler(function(theme) {
33 | color = theme === "default" ? "black" : "white";
34 | });
35 | }
36 |
37 | // Load the requested view
38 | switch (view) {
39 | case "content":
40 | renderSideBar(root);
41 | break;
42 | case "config":
43 | renderSettings(root);
44 | break;
45 | case "stage":
46 | default:
47 | try {
48 | renderStage(root);
49 | } catch (error) {
50 | renderError(root, error);
51 | }
52 | break;
53 | }
54 | }
55 |
56 | // STAGE VIEW
57 | const stageTemplate = document.createElement("template");
58 |
59 | stageTemplate["innerHTML"] = `
60 |
61 | `;
62 |
63 | function renderStage(elem) {
64 | elem.appendChild(stageTemplate.content.cloneNode(true));
65 |
66 | var __EVAL = s => eval(`void (__EVAL = ${__EVAL.toString()}); ${s}`);
67 |
68 | function evaluate(expr) {
69 | try {
70 | const result = __EVAL(expr);
71 | console.log(expr, '===>', result)
72 | } catch(err) {
73 | console.log(expr, 'ERROR:', err.message)
74 | }
75 | }
76 |
77 | var socket = io();
78 | var scene;
79 | var camera;
80 | var light;
81 | var JARVIB_sphere;
82 |
83 | const canvas = document.getElementById("renderCanvas"); // Get the canvas element
84 | var engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine
85 | const createScene = function () {
86 | // Creates a basic Babylon Scene object
87 | scene = new BABYLON.Scene(engine);
88 |
89 | // Create a default skybox with an environment.
90 | var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("https://playground.babylonjs.com/textures/environment.dds", scene);
91 | var currentSkybox = scene.createDefaultSkybox(hdrTexture, true);
92 |
93 | camera = new window.BABYLON.ArcRotateCamera(
94 | "camera",
95 | -Math.PI / 2,
96 | Math.PI / 2.5,
97 | 15,
98 | new window.BABYLON.Vector3(0, 0, 0)
99 | );
100 | // Targets the camera to scene origin
101 | camera.setTarget(BABYLON.Vector3.Zero());
102 | // This attaches the camera to the canvas
103 | camera.attachControl(canvas, true);
104 | // Creates a light, aiming 0,1,0 - to the sky
105 | light = new BABYLON.HemisphericLight("light",
106 | new BABYLON.Vector3(0, 1, 0), scene);
107 | // Dim the light a small amount - 0 to 1
108 | light.intensity = 0.8;
109 |
110 | JARVIB_sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter:2}, scene);
111 |
112 | socket.on('execute code', function(msg) {
113 | console.log(msg);
114 | evaluate(msg);
115 | });
116 |
117 | return scene;
118 | };
119 |
120 | // Access the microphone and create an analyser node
121 | navigator.mediaDevices.getUserMedia({ audio: true, video: false })
122 | .then(function(stream) {
123 | var audioContext = new (window.AudioContext || window.webkitAudioContext)();
124 | var microphone = audioContext.createMediaStreamSource(stream);
125 | var analyser = audioContext.createAnalyser();
126 | analyser.fftSize = 1024;
127 | microphone.connect(analyser);
128 |
129 | // Create a data array to store the frequency data
130 | var dataArray = new Uint8Array(analyser.frequencyBinCount);
131 |
132 | // Create a function to animate the sphere based on the frequency data
133 | function animate() {
134 | analyser.getByteFrequencyData(dataArray);
135 |
136 | // Use the average frequency to scale the sphere
137 | var sum = dataArray.reduce(function(a, b) { return a + b; });
138 | var avg = sum / dataArray.length;
139 | if (JARVIB_sphere) {
140 | JARVIB_sphere.scaling = new BABYLON.Vector3(avg/100, avg/100, avg/100);
141 | }
142 |
143 | requestAnimationFrame(animate);
144 | }
145 |
146 | animate();
147 | })
148 | .catch(function(err) {
149 | console.log('An error occurred: ' + err);
150 | });
151 |
152 | var scene = createScene(); //Call the createScene function
153 | // Register a render loop to repeatedly render the scene
154 | engine.runRenderLoop(function () {
155 | scene.render();
156 | });
157 | // Watch for browser/canvas resize events
158 | window.addEventListener("resize", function () {
159 | engine.resize();
160 | });
161 | }
162 |
163 | // SIDEBAR VIEW
164 | const sideBarTemplate = document.createElement("template");
165 |
166 | function renderSideBar(elem) {
167 | sideBarTemplate["innerHTML"] = `
168 |
173 |
Lets get started
175 |Press the share to meeting button.
176 | 177 |Welcome to J.A.R.V.I.B.!
208 |Press the save button to continue.
209 |Something went wrong
239 | 240 | 241 |${codeToExecute.code}`); 208 | 209 | return ''; 210 | }); 211 | 212 | interface Item { 213 | name: string; 214 | imageUrl: string; 215 | } 216 | 217 | interface TextBlock { 218 | type: "TextBlock"; 219 | text: string; 220 | weight?: "bolder"; 221 | size?: "large"; 222 | } 223 | 224 | interface Image { 225 | type: "Image"; 226 | url: string; 227 | width: string; 228 | horizontalAlignment: "left"; 229 | } 230 | 231 | interface Container { 232 | type: "Container"; 233 | items: (TextBlock | Image)[]; 234 | } 235 | 236 | interface AdaptiveCard { 237 | $schema: string; 238 | version: string; 239 | type: "AdaptiveCard"; 240 | body: (TextBlock | Container)[]; 241 | } 242 | 243 | app.ai.action('listAvailableModel', async (context: TurnContext, state: ApplicationTurnState, model: GetModel) => { 244 | console.dir(model); 245 | var modelName = model.nameOfTheModel ?? (