openai Archives - ProdSens.live https://prodsens.live/tag/openai/ News for Project Managers - PMI Tue, 14 May 2024 10:20:19 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 https://prodsens.live/wp-content/uploads/2022/09/prod.png openai Archives - ProdSens.live https://prodsens.live/tag/openai/ 32 32 OpenAI Unleashes GPT-4o! https://prodsens.live/2024/05/14/openai-unleashes-gpt-4o/?utm_source=rss&utm_medium=rss&utm_campaign=openai-unleashes-gpt-4o https://prodsens.live/2024/05/14/openai-unleashes-gpt-4o/#respond Tue, 14 May 2024 10:20:19 +0000 https://prodsens.live/2024/05/14/openai-unleashes-gpt-4o/ openai-unleashes-gpt-4o!

The future of AI just got a whole lot more exciting (and maybe a little bit scary)! Yesterday,…

The post OpenAI Unleashes GPT-4o! appeared first on ProdSens.live.

]]>
openai-unleashes-gpt-4o!

The future of AI just got a whole lot more exciting (and maybe a little bit scary)! Yesterday, OpenAI unveiled GPT-4o, a groundbreaking update to their powerful language model.

This isn’t just an incremental improvement – GPT-4o is a game-changer. It’s faster, more accessible, and boasts incredible new features like:

• Multimodal Magic: Interact with GPT-4o through text, voice, or even images! ️ Imagine seamlessly flowing from a written conversation to a visual representation of your ideas.

• Supercharged Assistant: ChatGPT just got a serious upgrade. GPT-4o can be your personal assistant on steroids, helping you with conversations, translations, and even creative tasks like generating images and sounds.

• Democratizing AI: OpenAI is making GPT-4o more accessible, with free tiers available for everyone to experiment with!

But wait, there’s more! There are also some important discussions happening around the potential risks of such powerful AI. OpenAI is committed to safety, but it’s a conversation we all need to be a part of.

Want to see GPT-4o in action? Check out the mind-blowing product presentation video here: https://www.youtube.com/live/DQacCB9tDaw?si=LmG3B-ps3VhU2-62

GenerativeAI

The post OpenAI Unleashes GPT-4o! appeared first on ProdSens.live.

]]>
https://prodsens.live/2024/05/14/openai-unleashes-gpt-4o/feed/ 0
Make the OpenAI Function Calling Work Better and Cheaper with a Two-Step Function Call 🚀 https://prodsens.live/2024/03/10/make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-%f0%9f%9a%80/?utm_source=rss&utm_medium=rss&utm_campaign=make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-%25f0%259f%259a%2580 https://prodsens.live/2024/03/10/make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-%f0%9f%9a%80/#respond Sun, 10 Mar 2024 11:20:48 +0000 https://prodsens.live/2024/03/10/make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-%f0%9f%9a%80/ make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-

I tried using OpenAI’s feature for running local functions in a project with many functions. It worked well,…

The post Make the OpenAI Function Calling Work Better and Cheaper with a Two-Step Function Call 🚀 appeared first on ProdSens.live.

]]>
make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-

I tried using OpenAI’s feature for running local functions in a project with many functions. It worked well, even with lots of functions, but the cost of using OpenAI’s API increased significantly. This happened because when we send function details (in JSON format) along with our main request, it counts as part of our input tokens, making it more expensive. The problem is, we often send more functions than necessary, even though the AI only needs a few of them to respond to our request.

So, I had an idea to save money and improve performace: What if we only send the details of the functions the AI actually needs? Here’s how it works: First, we send a request with our main question or task, including a list of all the functions we could use, but we don’t send the detailed instructions for those functions yet. We just give a brief description of each. Then, the AI tells us exactly which functions it needs to answer our question or complete our task. After that, we send another request with the detailed instructions only for those needed functions.

This method can significantly lower the cost per message because we only send the details for the functions that are necessary.

From another point of view, this method also makes the AI work faster and better. When we send too many detailed functions, we end up giving the AI too much information to handle at once. This can slow down its performance because it has to deal with a lot of extra details. However, if we only send the essential information that the AI needs, we help it stay focused and efficient. This way, we can even add more useful information without overloading it. For example, when using GPT-3 with many functions, we quickly hit the maximum amount of information it can consider at one time. By being selective about what we send, we avoid reaching this limit too soon.

Basic tool call example

Let’s look at a basic example of how function calling works. We start by sending the AI a question or task along with a list of all function schemas it can use. If the AI needs more information or has to do a specific job to answer our question, it picks one of the functions we’ve given it to help find the answer.

Maurer Krisztian

When you use tool or function calling, you’re essentially giving the AI model a way to ‘call out’ to an external function. This could be anything from performing a complex mathematical calculation, accessing a database for specific information, running a custom algorithm, or even interacting with web services. The function executes the task and returns the result to the AI, which then incorporates this information into its response.
https://platform.openai.com/docs/guides/function-calling

Two-Step Tool Call example

Maurer Krisztian two step tool call diagram

The process is simpler than it sounds, and here’s a straightforward explanation:

Start with a Single Tool: We begin by using a special tool called the “tool descriptor.” This tool takes one parameter, a list named neededTools, which specifies the tools that might be needed. You list all the available tools here.

Requesting Specific Tools: If the AI determines it needs certain tools to complete its task, it requests them through the “tool descriptor” by specifying which tools it needs from the neededTools list.

Providing the Requested Tools: Once the AI requests specific tools, we then supply these requested tools to the AI.

AI Uses the Tools: Now that the AI has the tools it specifically asked for, it can go ahead and process the request, using the tools as needed to come up with a final answer. Occasionally, during this process, the AI might realize it needs an additional tool it didn’t request initially. If that happens, the process starts over, and we provide the newly requested tool.

Here’s a simple way to look at it using an example: Imagine we have 100 tools available, but the AI only needs 2 to answer a question. Instead of sending all 100 tool descriptions upfront, we initially send just the “tool descriptor” request. Then, based on the AI’s needs, we only provide the 2 necessary tools. By using just 3 tool JSON schemas instead of 100, we save resources and make things more efficient. This approach uses fewer tokens, which is cheaper, and it also boosts performance. Having too many details can actually make the AI less accurate.

Check out how this method works with this code example: https://github.com/MaurerKrisztian/two-step-llm-tool-call

Thanks for taking the time to read! I hope you found it helpful. If you’re interested in seeing how to do this in Python, just let me know in the comments.

The post Make the OpenAI Function Calling Work Better and Cheaper with a Two-Step Function Call 🚀 appeared first on ProdSens.live.

]]>
https://prodsens.live/2024/03/10/make-the-openai-function-calling-work-better-and-cheaper-with-a-two-step-function-call-%f0%9f%9a%80/feed/ 0
LLM Development with JavaScript: Is that a thing? https://prodsens.live/2024/03/07/llm-development-with-javascript-is-that-a-thing/?utm_source=rss&utm_medium=rss&utm_campaign=llm-development-with-javascript-is-that-a-thing https://prodsens.live/2024/03/07/llm-development-with-javascript-is-that-a-thing/#respond Thu, 07 Mar 2024 23:20:23 +0000 https://prodsens.live/2024/03/07/llm-development-with-javascript-is-that-a-thing/ llm-development-with-javascript:-is-that-a-thing?

This tutorial is a fast track to developing JavaScript apps that talk to LLM models. You’ll have a…

The post LLM Development with JavaScript: Is that a thing? appeared first on ProdSens.live.

]]>
llm-development-with-javascript:-is-that-a-thing?

This tutorial is a fast track to developing JavaScript apps that talk to LLM models. You’ll have a REST service up and talking to an LLM in under 10 minutes. Let the coding magic begin!

This is part 1 from my free e-book:

The Busy Developers Guide to Generative AI

Fig 1: The Busy Developers Guide to Gen AI

All source code is available on GitHub.

ChatGPT vaulted generative AI into mainstream culture. But, it’s really just a user interface, powered by the true marvel that lies beneath - the large language model (LLM). 

More precisely, LLMs are very large deep learning models that are pre-trained on vast amounts of data. The keyword there is pre-trained. 

All we have to do to make use of these same models is send them a prompt telling it what we want. We can do that by calling the OpenAI APIs.

Fig 2: Your REST service can call the same APIs used by ChatGPT

1. Install Node

Download and install: https://nodejs.org. Verify the install in your terminal:

~ % node -v

If the installation succeeded, the version will print.

2. Initialize your project

Create a new directory for your project. Navigate to it in your terminal and run the following command:

~/ai-for-devs % npm init -y

This creates a new package.json file, initializing the project.

  1. Install Node modules

The node modules we’ll be using:

  • express: which makes server creation quick and easy
  • langchain: which provides a framework for building Apps with LLMs
  • @langchain/openai: which provides OpenAI integrations through their SDK
  • cors: Express middleware to enable CORS

In the same terminal, run the following command:

~/ai-for-devs % npm install express langchain @langchain/openai cors

4. Create the server file

Create a file called server.mjs in the project directory. Open it in a text editor and add the following lines of code:

import express from "express";
import { ChatOpenAI } from "@langchain/openai";
import cors from 'cors';

const app = express();

app.use(cors());

const chatModel = new ChatOpenAI({});

app.get('/', async (req, res) => {
  const response = 
    await chatModel.invoke(
      "Can you simply say 'test'?");

  res.send(response.content);
});

app.listen(3000, () => {
  console.log(`Server is running on port 3000`);
});

5. Create an OpenAI account

Register here: https://platform.openai.com. Obtain an API key:

  • Simply select ‘API keys’ in the upper left navigation
  • Select ‘+ Create new secret key’
  • Copy the key somewhere safe for now

Fig 3: The API keys in Open AI's interface

6. Set an environment variable

In the same terminal, run the following command with your key value:

~/ai-for-devs % export OPENAI_API_KEY=

Optionally add this command to your bash profile: ~/.zshrc

7. Launch your server

Back in the terminal, run the following command:

~/node-openai % node server.mjs

Open your web browser and visit: http://localhost:3000

You’ll see the response from the OpenAI model: “test”

Congratulations!

You’ve successfully built a functional REST service. Beyond its ability to prompt an AI and generate responses, it forms the foundation for the remainder of my free to download e-book:

The Busy Developer’s Guide to Gen AI

In Part 2, we’ll explore the process of streaming longer responses so our users don’t have to wait. Part 3 and part 4 will guide you through creating a complete RAG (Retrieval Augmented Generation) implementation.

Download the book to learn more!

The post LLM Development with JavaScript: Is that a thing? appeared first on ProdSens.live.

]]>
https://prodsens.live/2024/03/07/llm-development-with-javascript-is-that-a-thing/feed/ 0
How to create a dynamic AI Discord bot with TypeScript https://prodsens.live/2024/02/19/how-to-create-a-dynamic-ai-discord-bot-with-typescript/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-create-a-dynamic-ai-discord-bot-with-typescript https://prodsens.live/2024/02/19/how-to-create-a-dynamic-ai-discord-bot-with-typescript/#respond Mon, 19 Feb 2024 00:20:40 +0000 https://prodsens.live/2024/02/19/how-to-create-a-dynamic-ai-discord-bot-with-typescript/ how-to-create-a-dynamic-ai-discord-bot-with-typescript

Learn how to create your own AI Discord bot (with command and event handling) that can be dynamically…

The post How to create a dynamic AI Discord bot with TypeScript appeared first on ProdSens.live.

]]>
how-to-create-a-dynamic-ai-discord-bot-with-typescript

Learn how to create your own AI Discord bot (with command and event handling) that can be dynamically configurable through each guild.

Concepts that will be explored throughout this tutorial

Getting started

Project initialization

  • Create an empty folder for your project and initialize it (for this project, I’ll be using pnpm, but feel free to use whatever you prefer):
pnpm init
  • Install the dependencies and dev dependencies we’ll be using to get started:
pnpm add -D typescript ts-node
pnpm add discord.js nodemon dotenv mongoose openai
  • Now let’s set this up as a typescript project:
tsc --init
  • And make sure it has our specific configurations:
{
  "compilerOptions": {
      "lib": [
          "ESNext"
      ],
      "module": "CommonJS",
      "moduleResolution": "node",
      "target": "ESNext",
      "outDir": "dist",
      "sourceMap": false,
      "resolveJsonModule": true,
      "esModuleInterop": true,
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true,
      "allowSyntheticDefaultImports": true,
      "skipLibCheck": true,
      "skipDefaultLibCheck": true,
      "importHelpers": true,
  },
  "include": [
      "src/**/*",
      "environment.d.ts"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}
  • And let’s add these scripts to our package.json so we can run the project:
"scripts": {
    "start": "ts-node src/index.ts",
    "start:dev": "ts-node-dev src/index.ts",
    "start:prod": "node dist/index.js",
    "dev": "nodemon ./src/index.ts",
    "build": "tsc",
    "watch": "tsc -w"
  },
  • Time to create a ~/src/index.ts file and test that our project runs properly:
console.log("Hello World");
  • If we run pnpm dev and see Hello World in the console, it seems like our project environment is ready!

Getting our .env variables

Discord

  • Navigate to the Discord Developer Portal
  • Create a new application
  • Reset and copy the Bot’s token and add it to your .env
  • Make sure to enable the necessary presence intents that you’d like your Discord bot to be able to access.

OpenAI

  • Navigate to your OpenAI API Keys
  • Create a new API key and add it to your .env
  • Then copy your organization ID and add it as well (found here

MongoDB

  • Navigate to MongoDB Cloud
  • Create a new project and database then click Connect
    • Click Drivers
    • Copy your connection URI
    • Replace with your password
  • Add the URI to your .env

  • Create a ~/src/lib/db.ts file which will contain your MongoDB connection:

import "colors";
import mongoose from "mongoose";

const mongoURI = process.env.MONGO_URI;

const db = async () => {
    if (!mongoURI) {
        console.log(`[WARNING] Missing MONGO_URI environment variable!`.bgRed);
    }

    mongoose.set("strictQuery", true);

    try {
        if (await mongoose.connect(mongoURI)) {
            console.log(`[INFO] Connected to the database!`.bgCyan);
        }
    } catch (err) {
        console.log(`[ERROR] Couldn't establish a MongoDB connection!n${err}`.red);
    }
}

export default db;

Optional: install colors to add some color to your console.log‘s

Discord bot setup

We will need to set up a couple of folders and structures that will manage our Discord bot’s events and commands.

Utility

  • Create a utils/ folder within your src/ directory which will contain multiple utility functions to help the management of our Discord bot.

We are going to create a couple utility files that will be useful within this project; but to start, we need a function that can read files within folders.

import fs from "fs";
import path from "path";

/* 
    this function will accept 2 (one is optional) parameters:
    (1) the directory of which to read the files
    (2) if the function should read folders only, which we'll set as false by default
*/

const getFiles = (directory: string, foldersOnly = false) => {
    let fileNames = [];

    const files = fs.readdirSync(directory, { withFileTypes: true });

    for (const file of files) {
        const filePath = path.join(directory, file.name);

        if (foldersOnly) {
            if (file.isDirectory()) {
                fileNames.push(filePath);
            }
        } else {
            if (file.isFile()) {
                fileNames.push(filePath);
            }
        }
    }

    return fileNames;
}

export default getFiles;

Handler(s)

  • Create a /handlers/index.ts within your src/ directory:
    • For now, we will just add this eventHandler() function, but this can be expanded later for your needs.

This function will accept a discord Client parameter which will then read and register events that will be located within an events/ folder

import { Client } from "discord.js";
import path from "path";
import getFiles from "../utils/getFiles";

const eventHandler = (client: Client) => {
    const eventFolders = getFiles(path.join(__dirname, "..", "events"), true);

    for (const eventFolder of eventFolders) {
        const eventFiles = getFiles(eventFolder);

        let eventName: string;

        eventName = eventFolder.replace(/\/g, '/').split("/").pop();

        eventName === "validations" ? (eventName = "interactionCreate") : eventName;

        client.on(eventName, async (args) => {
            for (const eventFile of eventFiles) {
                const eventFunction = require(eventFile);
                await eventFunction(client, args);
            }
        })
    }
}

export default eventHandler;

Events

Now that we’ve established a function that can read and register events for the bot, let’s set up some events we want to listen for.

Firstly we’ll want our bot to listen for the ready event, if you’ve ever seen:

client.on("ready", () => {};

This is exactly what we’re setting up.

  • Create a ready/ folder within events/. Then inside this folder, we can put a file for each function we want to run when the bot is ready.
    • To start, I want the bot to console.log() when it’s ready, so I’m going to create a consoleLog.ts file:
import "colors";
import { Client } from "discord.js";

module.exports = (client: Client) => {
    console.log(`[INFO] ${client.user.username} is online!`.bgCyan);
}

IMPORTANT:

When exporting these functions so that they’re registered, we need to use module.exports since our eventHandler() function uses require()

Before continuing, we should now test and see if our bot will listen for this event:

  • Navigate to your src/index.ts file and register events to your bot:
import { config } from "dotenv";
import { Client, GatewayIntentBits } from "discord.js";
import eventHandler from "@/handlers";

config() // Load environment variables

const client = new Client({
    intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.GuildMembers
    ] // Specify all the intents you wish your bot to access
});

eventHandler(client) // Register events

client.login(process.env.BOT_TOKEN); // Login to the bot
  • If you run pnpm dev and see the console log, it looks like everything is working properly.

Let’s also connect to the database whenever the bot is ready.

  • Add a dbConnect.ts file to events/ready
import "colors";
import db from "../../lib/db";

module.exports = async () => {
    await db().catch((err) => console.log(`[ERROR] Error connecting to database! n${err}`.red));
}

Obviously, you’re gonna want to have the ability to create/delete/edit commands. So, if we’re gonna keep commands in, say, a commands/ folder, let’s create some utility functions that can gather those for us.

Commands

  • Create a file utils/getCommands.ts where we’re going to have 2 essential functions getApplicationCommands() and getLocalCommands()
    • getApplicationCommands() this function will find the commands that are already registered to the bot.
    • getLocalCommands() this function will fetch the commands from the commands/ folder.

Get commands

import { ApplicationCommandManager, Client, GuildApplicationCommandManager } from "discord.js";
import path from "path";
import getFiles from "./getFiles";


const getApplicationCommands = async (client: Client, guildId?: string) => {
    let applicationCommands: GuildApplicationCommandManager | ApplicationCommandManager;

    if (guildId) { // if registering to a specific guild
        const guild = await client.guilds.fetch(guildId);
        applicationCommands = guild.commands;
    } else {
        applicationCommands = client.application.commands;
    }

    await applicationCommands.fetch({
        guildId: guildId
    });

    return applicationCommands;
}

const getLocalCommands = (exceptions = []) => {
    let localCommands = [];

    const commandCategories = getFiles(path.join(__dirname, "..", "commands"), true);

    for (const commandCategory of commandCategories) {
        const commandFiles = getFiles(commandCategory);

        for (const commandFile of commandFiles) {
            const commandObject = require(commandFile);

            if (exceptions.includes(commandObject.name)) continue;
            localCommands.push(commandObject);
        }
    }

    return localCommands;
}

export {
    getApplicationCommands,
    getLocalCommands
};

Command type

Suppose we want our commands to look like:

import { PermissionsBitField, SlashCommandBuilder } from "discord.js";

const ping = {
    data: new SlashCommandBuilder()
        .setName("ping")
        .setDescription("Pong!")
        .addUserOption((option) => option
            .setName("user")
            .setDescription("The user you want to ping")
    ),
    userPermissions: [PermissionsBitField.Flags.SendMessages], // array of permissions the user needs to execute the command
    botPermissions: [PermissionsBitField.Flags.SendMessages], // array of permissions the bot needs to execute the command
    run: async (client, interaction) => {
        // run the command
    }
}

module.exports = ping;

Since we’re using TypeScript, let’s go ahead and create a type for our commands:

import { ChatInputCommandInteraction, Client, RESTPostAPIChatInputApplicationCommandsJSONBody, SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder } from "discord.js";

export type SlashCommand = {
    data: RESTPostAPIChatInputApplicationCommandsJSONBody | Omit<SlashCommandBuilder, "addSubcommandGroup" | "addSubcommand">
    | SlashCommandSubcommandsOnlyBuilder;
    userPermissions: Array<bigint>;
    botPermissions: Array<bigint>;
    run: (client: Client, interaction: ChatInputCommandInteraction) => Promise<any>;
}

Alright, we’re ALMOST ready to create an event that’ll handle registering commands…

But, unless you wanna re-register every command from the commands folder every time the bot is online, we’ll need some sort of function that’s going to compare the locally existing commands (commands/) to the commands that have been already registered to the bot.

Command compare

  • Create a file within utils/ that will hold our commandCompare() function

commandCompare()

import { ApplicationCommand } from "discord.js";
import { SlashCommand } from "./types";

const commandCompare = (existing: ApplicationCommand, local: SlashCommand) => {
    const changed = (a, b) => JSON.stringify(a) !== JSON.stringify(b);

    if (changed(existing.name, local.data.name) || changed(existing.description, local.data.description)) {
        return true;
    }

    function optionsArray(cmd) {
        const cleanObject = obj => {
            for (const key in obj) {
                if (typeof obj[key] === 'object') {
                    cleanObject(obj[key]);

                    if (!obj[key] || (Array.isArray(obj[key]) && obj[key].length === 0)) {
                        delete obj[key];
                    }
                } else if (obj[key] === undefined) {
                    delete obj[key];
                }
            }
        };

        const normalizedObject = (input) => {
            if (Array.isArray(input)) {
                return input.map((item) => normalizedObject(item));
            }

            const normalizedItem = {
                type: input.type,
                name: input.name,
                description: input.description,
                options: input.options ? normalizedObject(input.options) : undefined,
                required: input.required
            }

            return normalizedItem;
        }

        return (cmd.options || []).map((option) => {
            let cleanedOption = JSON.parse(JSON.stringify(option));
            cleanedOption.options ? (cleanedOption.options = normalizedObject(cleanedOption.options)) : (cleanedOption = normalizedObject(cleanedOption));
            cleanObject(cleanedOption);
            return {
                ...cleanedOption,
                choices: cleanedOption.choices ? JSON.stringify(cleanedOption.choices.map((c) => c.value)) : null
            }
        })
    }

    const optionsChanged = changed(optionsArray(existing), optionsArray(local.data));

    return optionsChanged;
}

export default commandCompare;

Validations (interactionCreate)

Circling back to the eventHandler(), do you remember this line:

eventName === "validations" ? (eventName = "interactionCreate") : eventName;

This was intended so we can validate the commands. We’ll add a file within events validations/command.ts which will attempt to notify users if the bot and/or the user has insufficient permissions to use the command, or otherwise run the command.

validations

import "colors";
import { Client, ColorResolvable, CommandInteraction, EmbedBuilder, Colors } from "discord.js";
import { getLocalCommands } from "../../utils/getCommands";
import { SlashCommand } from "../../utils/types";

module.exports = async (client: Client, interaction: CommandInteraction) => {

    if (!interaction.isChatInputCommand()) return;

    const localCommands = getLocalCommands();
    const commandObject: SlashCommand = localCommands.find((cmd: SlashCommand) => cmd.data.name === interaction.commandName);

    if (!commandObject) return;

    const createEmbed = (color: string | ColorResolvable, description: string) => new EmbedBuilder()
        .setColor(color as ColorResolvable)
        .setDescription(description);

    for (const permission of commandObject.userPermissions || []) {
        if (!interaction.memberPermissions.has(permission)) {
            const embed = createEmbed(Colors.Red, "You do not have permission to execute this command!");

            return await interaction.reply({ embeds: [embed], ephemeral: true });
        }
    }

    const bot = interaction.guild.members.me;

    for (const permission of commandObject.botPermissions || []) {
        if (!bot.permissions.has(permission)) {
            const embed = createEmbed(Colors.Red, "I don't have permission to execute this command!");

            return await interaction.reply({ embeds: [embed], ephemeral: true });
        }
    }

    try {
        await commandObject.run(client, interaction);
    } catch (err) {
        console.log(`[ERROR] An error occured while validating commands!n ${err}`.red);
        console.error(err);
    }
}

Register commands

Now we can add an event within events/ready/ that will register (add, delete, edit) the commands!

registerCommands()

import "colors";
import { Client } from "discord.js";
import commandCompare from "../../utils/commandCompare";
import { getApplicationCommands, getLocalCommands } from "../../utils/getCommands";

module.exports = async (client: Client) => {
    try {

        const [localCommands, applicationCommands] = await Promise.all([
            getLocalCommands(),
            getApplicationCommands(client)
        ]);

        for (const localCommand of localCommands) {
            const { data, deleted } = localCommand;
            const { name: commandName, description: commandDescription, options: commandOptions } = data;

            const existingCommand = applicationCommands.cache.find((cmd) => cmd.name === commandName);

            if (deleted) {
                if (existingCommand) {
                    await applicationCommands.delete(existingCommand.id);
                    console.log(`[COMMAND] Application command ${commandName} has been deleted!`.grey);
                } else {
                    console.log(`[COMMAND] Application command ${commandName} has been skipped!`.grey);
                }
            } else if (existingCommand) {
                if (commandCompare(existingCommand, localCommand)) {
                    await applicationCommands.edit(existingCommand.id, {
                        name: commandName, description: commandDescription, options: commandOptions
                    });
                    console.log(`[COMMAND] Application command ${commandName} has been edited!`.grey);
                }
            } else {
                await applicationCommands.create({
                    name: commandName, description: commandDescription, options: commandOptions
                });
                console.log(`[COMMAND] Application command ${commandName} has been registered!`.grey);
            }
        }

    } catch (err) {
        console.log(`[ERROR] There was an error inside the command registry!n ${err}`.red);
    }
}

Creating commands

It’s time to create the first command to see if it registers when our bot is ready.

NOTE:

You can create sub-folders for categories of commands.

commands/misc/ping.ts

import { SlashCommand } from "../../utils/types";
import { EmbedBuilder, SlashCommandBuilder, userMention, Colors } from "discord.js";

const ping: SlashCommand = {
    data: new SlashCommandBuilder()
        .setName("ping")
        .setDescription("Ping a user")
        .setDMPermission(false)
        .addUserOption((option) => option
            .setName("user")
            .setDescription("The user you wish to ping")
            .setRequired(true),
    ),
    userPermissions: [],
    botPermissions: [],
    run: async (client, interaction) => {
        const options = interaction.options;
        const target = options.getUser("user");

        const embed = new EmbedBuilder()
            .setDescription(userMention(target.id))
            .setColor(Colors.Default)

        return await interaction.reply({ embeds: [embed] });
    },
}

module.exports = ping;

You should be able to start up your bot and see:

commands registered

And if I run the command:

ping command

We’re ready to start implementing the key features!

AI (OpenAI)

  • Create a file inside your lib/ directory to hold your OpenAI object:
import { OpenAI } from "openai";

const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY,
    organization: process.env.OPENAI_ORGANIZATION_ID,
});

export default openai;
  • Create another file for your OpenAI query:

query()

import openai from "./openai";

/*
    a sleep function to make sure the AI gets a good night's rest before it has to get back to work
*/
function sleep(ms: number) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

const query = async (prompt: string, guildId: string) => {

    /* 
        the `guildId` paramater will come in handy later when we want the bot to respond dynamically based on the guild's settings
    */

    if (!prompt || prompt.length < 1) return false;

    /*
        this variable directs the AI how to respond.
        it will be made dynamic later (along with all the other configurations)
    */
    const tempSystemRoleContent = "Respond to the given prompt in a funny and/or witty way."

    const res = await openai.chat.completions.create({
        model: "gpt-3.5-turbo",
        messages: [
            {
                role: "system",
                content: tempSystemRoleContent
            },
            {
                role: "user",
                content: prompt
            }
        ],
        temperature: 0.86,
        presence_penalty: 0
    })
        .then((res) => res.choices[0].message)
        .catch((err) => `Error with query!n${err}`);

    await sleep(1000);

    if (typeof res === 'object') {
        return res.content;
    }

    return res;
}

export default query;

  • Create a command just to test that the AI is working, I’m just going to call it /ai

/ai

import query from "../../lib/query";
import { SlashCommand } from "../../utils/types";
import { SlashCommandBuilder } from "discord.js";

const ai: SlashCommand = {
    data: new SlashCommandBuilder()
        .setName("ai")
        .setDescription("Say or ask something to an AI")
        .addStringOption((option) => option
            .setName("prompt")
            .setDescription("The prompt to give")
            .setRequired(true)
            .setMinLength(5)
            .setMaxLength(500)
    ),
    userPermissions: [],
    botPermissions: [],
    run: async (client, interaction) => {
        const { guildId } = interaction;

        if (!interaction.isCommand()) return;

        const prompt = interaction.options.getString("prompt");

        // defer the reply to give the openai query time
        await interaction.deferReply().catch(() => null)

        const response = await query(prompt, guildId);

        if (response === undefined || response === null || !response) {
            return await interaction.editReply({ content: "An error occured" })
        }
        if (interaction.replied) {
            return;
        }
        if (interaction.deferred) {
            return await interaction.editReply({ content: response });
        }

        return;
    }
}

module.exports = ai;

And as you can see, this should work for you with absolutely no errors:

ai command gif

Mongoose

When setting up the query() from before, we manually passed in certain options like:

Instead of having these variables set in stone, we’re going to allow administrators of guilds to alter the settings. To do that, we need to be able to store a guild’s settings within a database.

Firstly I’m going to envision the type of data to work with by creating a model.

models/guild.ts

import { model, Schema } from "mongoose";

const guildSchema = new Schema({
    GuildID: String,
    SystemRoleContent: String,
    Temperature: Number,
    PresencePenalty: Number,
}, { strict: false });

export default model("guild", guildSchema);

Then I set up a configuration file which contains the default settings to use, while adding logic to the query() function to check for a guild’s settings:

let guildData = await guild.findOne({ GuildID: guildId });

    if (!guildData) {
        guildData = new guild({
            GuildID: guildId,
            Temperature: config.openai.temperature,
            SystemRoleContent: config.openai.systemRoleContent,
            PresencePenalty: config.openai.presence_penalty,
            Model: config.openai.model,
        });

        await guildData.save();
    }

Then use those values within the chat completion function:

await openai.chat.completions.create({
        model: guildData.Model,
        messages: [
            {
                role: "system",
                content: guildData.SystemRoleContent
            },
            {
                role: "user",
                content: prompt
            }
        ],
        temperature: guildData.Temperature,
        presence_penalty: guildData.PresencePenalty
    })

Finally, create a command in which:

  • Only admins can use
  • Has sub commands:
    • /settings view
    • /settings config
    • /settings reset

/settings command

  • Only admins can use
userPermissions: [PermissionsBitField.Flags.Administrator]
  • Has sub commands:
.addSubcommand((sub) => sub
            .setName("reset")
            .setDescription("Reset your guild to default settings")
        )

For configuration options, make sure to add the parameters you want to the ability to be altered:

  • presence_penalty
.addNumberOption((option) => option
                .setName("presence_penalty")
                .setDescription("How diverse the responses are")
                .setMinValue(-2)
                .setMaxValue(2)
            )

run()

Get the sub command used & the guild, then fetch the guild’s data model

const { options, guildId } = interaction;

        const subCommand = options.getSubcommand(true);

        if (!["config", "view", "reset", "help"].includes(subCommand)) return;
import Guild from "../../models/guild";
// ...

let guildData = await Guild.findOne({
            GuildID: guildId
        });

        if (!guildData) {
            guildData = new Guild({
                GuildID: guildId,
                // ...
            });

            await guildData.save();
        }

Then you can configure the guild’s settings by utilizing .updateOne():

await guildData.updateOne({ /* 
*/
})

View the complete code for the way I set up my settings command here.

You now have a customizable AI bot!

Ideas to expand

  • Apply additional functionality to check for models/configure the model.
  • Integrate into a website (nextjs?)
  • Create a custom authentication page
  • Set up logic so the default settings change based on prompts

Thank you for reading my first post on here! The full code can be found on my github here.

The bot is live and running now on an AWS instance. There are steps listed in the repository’s readme with how to go about getting your bot live at all times.

Feel free to make any issues and/or pull requests, or leave feedback with any thoughts!

The post How to create a dynamic AI Discord bot with TypeScript appeared first on ProdSens.live.

]]>
https://prodsens.live/2024/02/19/how-to-create-a-dynamic-ai-discord-bot-with-typescript/feed/ 0
Taking an Open Source Project to Release 1.1 🚀 https://prodsens.live/2024/02/03/taking-an-open-source-project-to-release-1-1-%f0%9f%9a%80/?utm_source=rss&utm_medium=rss&utm_campaign=taking-an-open-source-project-to-release-1-1-%25f0%259f%259a%2580 https://prodsens.live/2024/02/03/taking-an-open-source-project-to-release-1-1-%f0%9f%9a%80/#respond Sat, 03 Feb 2024 23:20:29 +0000 https://prodsens.live/2024/02/03/taking-an-open-source-project-to-release-1-1-%f0%9f%9a%80/ taking-an-open-source-project-to-release-1.1-

In my last post, I discussed how I integrated Text to Speech support into ChatCraft using OpenAI’s TTS…

The post Taking an Open Source Project to Release 1.1 🚀 appeared first on ProdSens.live.

]]>
taking-an-open-source-project-to-release-1.1-

In my last post, I discussed how I integrated Text to Speech support into ChatCraft using OpenAI’s TTS API.

In this post, I’ll share my progress on that Pull Request and other contributions I made as a part of releasing v1.1 of the project.

1.1

Table of Contents

 1. Text to Speech Support
 2. Filing and fixing follow ups
       2.1. Using Better icons for TTS button
       2.2. Fixing TTS toggle behaviour
       2.3. TTS should abort when switched off
 3. Helping with migrating the Menu Component
       3.1. Suggesting changes on GitHub
       3.2. Pushing Fixes
 4. Reviewing a Pull Request
 5. Release v1.1 🚀

Text to Speech Support

Before talking about other contributions, I was able to get an initial version of text to speech landed successfully after a few discussions and changes.

My professor was a little confused why I was managing Promises of audio urls instead of directly storing url strings in my audio queue. I tried to explain it with the help of an example.

Here’s the link if you’re interested:
https://github.com/tarasglek/chatcraft.org/pull/357#discussion_r1473470003

Explanation

PR Merged

I embedded a video demo in my last post, and you can try it yourself by visiting ChatCraft.

It was not perfect by any chance, and so I had to open a couple of follow up issues aimed to fix the technical shortcomings.

  1. https://github.com/tarasglek/chatcraft.org/issues/386
  2. https://github.com/tarasglek/chatcraft.org/issues/387

As soon as it was merged, I started receiving more feedback that I couldn’t even get in original reviews because now people were forced to use it 😉.

Feedback

And so, it was time to file even more follow ups aimed to address the issues brought up by Taras.

Filing and fixing follow ups

Various issues were encountered after my PR was merged.

Using Better icons for TTS button

The first one was that the icons I used for TTS enable/disable button didn’t do a good job at indicating the state of the feature.

I quickly opened an issue for that

Button Issue

and replaced the icons with Material Design variants as suggested.

New Icons

After a quick review, the Pull Request was merged.

Quick Review

Time to look at the other issues!

Fixing TTS toggle behaviour

The next thing on the list was a little annoying for the users.

TTS complaint

You read that RIGHT! For some reason, the text to speech functionality was always enabled no matter if the state of the toggle button.

Another follow up issue had to be opened.

Button Toggle Bug

The problem was that I wasn’t checking for the TTS setting in the else if branch in the following code:

if (isTtsSupported() && getSettings().announceMessages) {
  if (
    sentenceEndRegex.test(ttsWordsBuffer) // Has full sentence
  ) {
    // Reset lastIndex before calling exec
    sentenceEndRegex.lastIndex = 0;
    const sentenceEndIndex = sentenceEndRegex.exec(ttsWordsBuffer)!.index;

    // Pass the sentence to tts api for processing
    const textToBeProcessed = ttsWordsBuffer.slice(0, sentenceEndIndex + 1);
    const audioClipUri = textToSpeech(textToBeProcessed);
    addToAudioQueue(audioClipUri);

    // Update the tts Cursor
    ttsCursor += sentenceEndIndex + 1;
  } else if (ttsWordsBuffer.split(" ").length >= TTS_BUFFER_THRESHOLD) {
    // Flush the entire buffer into tts api
    const audioClipUri = textToSpeech(ttsWordsBuffer);
    addToAudioQueue(audioClipUri);

    ttsCursor += ttsWordsBuffer.length;
  }
}

I quickly opened a Pull Request for the fix.

Hotfix PR

and felt the need for adding a hotfix label to the repo

hotfix label

as this sure was one.

hotfix label discussion

TTS should abort when switched off

The third problem was that the TTS announcement always kept playing, even when the feature was turned off using the toggle button.

Third problem

I filed an issue for that

tts abort issue

but still need to work on it.

Here’s the state of follow ups so far.

State of follow ups 1

State of follow ups 2

Helping with migrating the Menu Component

The next big thing I worked on this week was helping Rachit with the creation of a new Menu Component for the application using the react-menu package.

Here’s some context for you guys.

Context for React Menu

Rachit was able to get the functionality working for our component wrapper, but needed some help with the styling part.

Since I was asked for help,

Need Help

I had to get into action.

Suggesting changes on GitHub

The first step was to review behaviour and suggest any initial changes that came to my mind.

I quickly noticed a weird behaviour that did not exist before

weird behaviour

I posted a suggestion on GitHub that could be directly committed to fix that issue.

Fixed Behaviour:

Fixed Behaviour

We continued with this GitHub Suggestions for a while

GitHub Suggestions

Pushing Fixes

Until professor suggested us another approach of collaboration.

another approach

This was exactly what I wanted to do, and now that I knew it was acceptable, I asked Rachit if could push directly to his branch. And pushed the fixes for anything I could think of.

pushed the fixes

For full details, please follow the Pull Request directly. This was the biggest conversation I ever I had in a PR.

biggest conversation

Reviewing a Pull Request

Last but not least, I reviewed a Pull Request Katie was working on, that would allow users to store metaData for multiple AI providers at once.

I made a simple suggestion on this one to create a url to provider name mapping instead of manually determining provider name with a switch statement.

This suggestion was accepted

suggestion was accepted

And after reviews from professor, this PR was also merged 🎊

this PR was also merged

Release v1.1 🚀

All this work, was just a part of contributions for Release 1.1 that happened yesterday.

We have made significant progress

significant progress

and here’s one for the new contributors 🍾🥂

one to the new contributors

The post Taking an Open Source Project to Release 1.1 🚀 appeared first on ProdSens.live.

]]>
https://prodsens.live/2024/02/03/taking-an-open-source-project-to-release-1-1-%f0%9f%9a%80/feed/ 0
Walles.AI Unleashed: A Rollercoaster Ride of Emotion in Every Keystroke https://prodsens.live/2024/01/16/walles-ai-unleashed-a-rollercoaster-ride-of-emotion-in-every-keystroke/?utm_source=rss&utm_medium=rss&utm_campaign=walles-ai-unleashed-a-rollercoaster-ride-of-emotion-in-every-keystroke https://prodsens.live/2024/01/16/walles-ai-unleashed-a-rollercoaster-ride-of-emotion-in-every-keystroke/#respond Tue, 16 Jan 2024 09:24:48 +0000 https://prodsens.live/2024/01/16/walles-ai-unleashed-a-rollercoaster-ride-of-emotion-in-every-keystroke/ walles.ai-unleashed:-a-rollercoaster-ride-of-emotion-in-every-keystroke

In the ever-evolving realm of artificial intelligence, Walles.AI stands out as a game-changer, not just for its technical…

The post Walles.AI Unleashed: A Rollercoaster Ride of Emotion in Every Keystroke appeared first on ProdSens.live.

]]>
walles.ai-unleashed:-a-rollercoaster-ride-of-emotion-in-every-keystroke

Image description

In the ever-evolving realm of artificial intelligence, Walles.AI stands out as a game-changer, not just for its technical prowess, but for its ability to infuse a rollercoaster of emotions into every keystroke. This revolutionary platform transcends traditional AI functionalities, offering users an experience that goes beyond the mundane and taps into the realm of emotions.

The Heartbeat of Walles.AI:

At the core of Walles.AI is an intricate web of algorithms designed not only to process data and generate insights but to understand and respond to the emotions embedded within the text. From joy to sorrow, excitement to contemplation, Walles.AI reads between the lines, deciphering the emotional nuances that make communication uniquely human.

Empathy in Automation:

Walles.AI redefines the relationship between technology and emotion by introducing a layer of empathy into automation. As users interact with the platform, whether through chat interfaces or data analysis, Walles.AI adapts its responses to match the emotional context of the conversation. This not only enhances user engagement but also creates a more human-like interaction, fostering a deeper connection between users and technology.

Image description

The Emotional Rollercoaster in Text Analysis:

Imagine a platform that not only understands the words you type but also grasps the emotions behind them. Walles.AI achieves this by employing advanced sentiment analysis algorithms that recognize and respond to the emotional tone of text. Whether it’s a celebratory achievement or a moment of frustration, Walles.AI rides the peaks and valleys of emotion, providing a nuanced and empathetic interaction.

Sentiment-Driven Decision Making:

Beyond just understanding emotions, Walles.AI leverages sentiment analysis to drive decision-making processes. For businesses, this means gauging customer satisfaction, employee morale, and market sentiment in real-time. By tapping into the emotional pulse of data, Walles.AI equips organizations with a dynamic tool for making informed decisions that resonate with the prevailing sentiments of their stakeholders.

The Symphony of Emotion and Creativity:

Walles.AI doesn’t just stop at decoding emotions; it also extends its capabilities into the realm of creativity. Through natural language generation, the platform crafts emotionally resonant content, be it marketing copy that inspires, social media posts that connect, or storytelling that evokes a visceral response. This integration of emotion and creativity transforms Walles.AI into a versatile tool for content creation across diverse industries.

Balancing Ethical Considerations:

While the infusion of emotion into artificial intelligence brings forth exciting possibilities, it also raises ethical considerations. Walles.AI takes a conscientious approach, prioritizing user privacy, consent, and responsible AI practices. The platform is designed to enhance user experiences ethically, ensuring that the emotional journey is not compromised by concerns over data security or misuse.

Conclusion:

Walles.AI’s foray into the emotional landscape of artificial intelligence marks a paradigm shift in how we interact with technology. By infusing emotion into every keystroke, this platform creates a dynamic and empathetic user experience, blurring the lines between man and machine. As we navigate the thrilling rollercoaster ride that is Walles.AI, we find ourselves at the intersection of technology and emotion, where each keystroke tells a story, and every interaction resonates with the heartbeat of humanity.

The post Walles.AI Unleashed: A Rollercoaster Ride of Emotion in Every Keystroke appeared first on ProdSens.live.

]]>
https://prodsens.live/2024/01/16/walles-ai-unleashed-a-rollercoaster-ride-of-emotion-in-every-keystroke/feed/ 0
How to Get Audio Transcriptions from Whisper without a File System https://prodsens.live/2023/12/10/how-to-get-audio-transcriptions-from-whisper-without-a-file-system/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-get-audio-transcriptions-from-whisper-without-a-file-system https://prodsens.live/2023/12/10/how-to-get-audio-transcriptions-from-whisper-without-a-file-system/#respond Sun, 10 Dec 2023 22:25:04 +0000 https://prodsens.live/2023/12/10/how-to-get-audio-transcriptions-from-whisper-without-a-file-system/ how-to-get-audio-transcriptions-from-whisper-without-a-file-system

Whisper is OpenAI’s intelligent speech-to-text transcription model. It allows developers to enter audio and an optional styling prompt,…

The post How to Get Audio Transcriptions from Whisper without a File System appeared first on ProdSens.live.

]]>
how-to-get-audio-transcriptions-from-whisper-without-a-file-system

Whisper is OpenAI’s intelligent speech-to-text transcription model. It allows developers to enter audio and an optional styling prompt, and get transcribed text in response.

However, the official OpenAI Node.js SDK API docs only show one way to use Whisper – reading an audio file with fs.

async function main() {
  const transcription = await openai.audio.transcriptions.create({
    file: fs.createReadStream("audio.mp3"),
    model: "whisper-1",
  });

  console.log(transcription.text);
}

That works fine if you have static files… but in any consumer application, we’ll be processing data from an end-user client such as an app or web browser. To receive audio from thousands of users and save it as files is a major waste of disk space and a huge ineffeciency. Plus, serverless deployment is extremely popular today, and in a serverless environment we usually don’t have persistent file storage. I wrote this article because it was surprisingly hard to figure out how to achieve audio transcription without saving the audio as a file first.

How to use Whisper without files

On the client-side, you’ll need to get your audio into a Base64 encoded string. I’m using the library “@ricky0123/vad-react” for this purpose, which comes with utilities to accomplish that:

onSpeechEnd: (audio) => {
      const wavBuffer = utils.encodeWAV(audio);
      const base64 = utils.arrayBufferToBase64(wavBuffer);
      const audioUrlAsData = `${base64}`;
      // chose POST here with a payload to ensure the Base64 string doesn't violate the max length of a URL
      fetch("/api/transcribe", {
        method: "POST",
        body: JSON.stringify({ audioData: audioUrlAsData }), 
      })
}

Then on the server-side, the trick is to create a buffer from the base64 data and use the undocumented toFile function from OpenAI’s library.

import OpenAI, { toFile } from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

export default async function handler(
  req,
  res
) {
  try {
    // Extract Base64 encoded data from the request
    const bodyData = JSON.parse(req.body);
    const base64Audio = bodyData.audioData;

    // Decode Base64 to binary
    const audioBuffer = Buffer.from(base64Audio, "base64");

    // Use OpenAI API to transcribe the audio
    const transcription = await openai.audio.transcriptions.create({
      file: await toFile(audioBuffer, "audio.wav", {
        contentType: "audio/wav",
      }),
      model: "whisper-1",
    });

    // Send the transcription text as response
    res.json({ transcription: transcription.text });
  } catch (error) {
    console.error("Error during transcription:", error);
    res.status(500).send("Error during transcription");
  }
}

Voila! Through this process, you can use Whisper without saving audio from every user as static files, allowing it to be used in a serverless environment.

The post How to Get Audio Transcriptions from Whisper without a File System appeared first on ProdSens.live.

]]>
https://prodsens.live/2023/12/10/how-to-get-audio-transcriptions-from-whisper-without-a-file-system/feed/ 0
Sam Altman: Corporate Collateral Damage? https://prodsens.live/2023/11/19/sam-altman-corporate-collateral-damage/?utm_source=rss&utm_medium=rss&utm_campaign=sam-altman-corporate-collateral-damage https://prodsens.live/2023/11/19/sam-altman-corporate-collateral-damage/#respond Sun, 19 Nov 2023 02:25:40 +0000 https://prodsens.live/2023/11/19/sam-altman-corporate-collateral-damage/ sam-altman:-corporate-collateral-damage?

My Contrarian Perspectives on Why Sam Altman was Dispensable. See Definition of Terms & Acronyms in Footnote. If…

The post Sam Altman: Corporate Collateral Damage? appeared first on ProdSens.live.

]]>
sam-altman:-corporate-collateral-damage?

My Contrarian Perspectives on Why Sam Altman was Dispensable.

See Definition of Terms & Acronyms in Footnote.

If you understand how the engines of corporate missions work, Sam Altman’s present dilemma will not come to you as a surprise.

Honestly, with his going about announcing how he was optimistic about building AGI soon, even asking Microsoft for the funding… he was only exposing himself as a threat to corporate dominance & profit.

Microsoft quickly scooped up OpenAI with their successful implementation of GPT for LLM. There was great potential, but the CEO, Sam Altman, like Nikola Tesla or Steve Jobs, seemed to have their head stuck in the clouds (becoming too ambitious).

How can he be talking about building AGI when the profit from ChatGPT (Narrow AI) had not been properly scooped… Microsoft is known for squeezing out every last drop from its service/subscription-based business. ChatGPT would be no exception.

At the moment, Microsoft has already begun plans for designing their own AI chips (outside of Nvidia), following after Google & Amazon.

With OpenAI’s technology success on the one hand and their ability to scale up processing, using their soon-to-be-released Super-processing Maia chips (105 Billion Transistors), I believe Microsoft wants to lockdown on the current Generative AI space.

For Microsoft, the race to pursue AGI will be run on another leg… 

Cleaning all the profit from Generative AI is a good enough goal.

Sam Altman, like Nikola Tesla in his day, was mostly concerned with advancing the reach of innovation, in which case, AGI is the next frontier. A noble ambition if you ask me (since I mostly fall within this frame of thinking).

I believe AGI will inadvertently be developed (maybe even by China, surprising how quiet they have been all this while), but I don’t think developing AGI is the goal of most corporate boards at the moment. Profit is always a priority for capitalist-induced boards (an unfortunate dilemma for human society if you ask me).

The concern of how quickly AGI can accelerate into ASI is hanging.

There is really a lot going under the hood of the AI space… I believe the future has caught up to us at a time when we are ill-prepared.

Transhumanism is quietly brewing at the side, and Quantum Computing may suddenly shock us one day with a shout of true Quantum Supremacy.

Nanotechnology and robotics are making their rounds in silence across labs worldwide. Extended Realities (AR/VR/MR) are awaiting the material collapse of society to fill in the void speciously. The truth is that technology is ready for a Singularity; humans are not.

Sorry for my veering off-topic. Back to the issue of Sam Altman. I believe there are numerous labs (though fringe) who are studiously working on developing AGI, though they wouldn’t have the advantage of mega financing, which Sam Altman thought he had secured through the Microsoft deal.

I think he failed to learn Nikola Tesla’s lesson - old men after profit do not change, and old dogs are not interested in new tricks.

Sam will be fine, and not AGI will save the world. It is humans learning not to prioritize profit & greed. Humans learn that technology, with its advantages, shouldn’t be taken from a selfish, self-preservative mindset –this will do us more good than ASI.

I hate that I am starting to sound idealistic, but… these are the truths we will all have to come to squarely as Technology keeps unveiling the reality of human nature.

Our biggest investment now should be in the re-rendering of human nature. Love, compassion & altruism… These are the things that make technology and even profit meaningful.

Foot Note: Definition of Terms

  • AI - Artificial Intelligence
  • AGI - Artificial General Intelligence
  • ASI - Artificial Super Intelligence
  • LLM - Large Language Models
  • GPT - Generative Pre-Trained Transformers

-Transhumanism - The idea that technology can help humans build real Utopia *My way of definition.

- kelly Idehen

The post Sam Altman: Corporate Collateral Damage? appeared first on ProdSens.live.

]]>
https://prodsens.live/2023/11/19/sam-altman-corporate-collateral-damage/feed/ 0
How to Make OpenAI API to Return JSON https://prodsens.live/2023/11/15/how-to-make-openai-api-to-return-json/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-make-openai-api-to-return-json https://prodsens.live/2023/11/15/how-to-make-openai-api-to-return-json/#respond Wed, 15 Nov 2023 18:25:34 +0000 https://prodsens.live/2023/11/15/how-to-make-openai-api-to-return-json/ how-to-make-openai-api-to-return-json

During OpenAI’s dev day, one of the major announcements was the ability to receive a JSON from the…

The post How to Make OpenAI API to Return JSON appeared first on ProdSens.live.

]]>
how-to-make-openai-api-to-return-json

During OpenAI’s dev day, one of the major announcements was the ability to receive a JSON from the chat completion API. However, there aren’t a few clear examples of how to do this as most examples focus on function calls.

Our objective is straightforward: given a query, we want to receive an answer in JSON format.

How can we achieve this?

There are three crucial steps.

Modify your prompt

Your prompt must explicitly specify that the response should be in JSON format and you need to define the structure of the JSON object.

Given this work history, what's the overall years of experience?
Work history includes start and end date (or present), title, and company name.
If the end date is "Present", use the current date. Today is November 2023.
Return the answer in JSON format with the field "experienceInMonths"
and value as a number.

Pay attention to the last sentence of the prompt.

Pass response_format

When calling the API, specify the response_format.

const res = await this.openAI.chat.completions.create({
  model: 'gpt-3.5-turbo-1106',
  temperature: 0.0,
  top_p: 1,
  frequency_penalty: 0,
  presence_penalty: 0,
  response_format: {
    type: 'json_object', // specify the format
  },
  messages: [
    { role: 'system', content: systemPrompt },
    { role: 'user', content: workHistory },
  ],
});

It’s crucial to modify the prompt as well. Just changing the response type to JSON might result in a JSON of an arbitrary structure.

See this comment from the OpenAI API:

**Important:** when using JSON mode, you **must** also instruct the model to
produce JSON yourself via a system or user message. Without this, the model may
generate an unending stream of whitespace until the generation reaches the token
limit, resulting in increased latency and the appearance of a "stuck" request.
Also note that the message content may be partially cut off if
`finish_reason="length"`, which indicates the generation exceeded `max_tokens`
or the conversation exceeded the max context length.

Parse JSON response

Once we receive the response, the content is still text (string type), but we can now parse it as JSON.

const content: string = get(res, 'choices[0].message.content');
try {
  return JSON.parse(content)['experienceInMonths'];
} catch (error) {
  this.logger.warn('Calculating total experience for a member did not work');
  return -1;
}

It’s good practice to wrap JSON.parse in a try…catch statement in case we receive an invalid JSON structure.

You can find a playground example here.

The post How to Make OpenAI API to Return JSON appeared first on ProdSens.live.

]]>
https://prodsens.live/2023/11/15/how-to-make-openai-api-to-return-json/feed/ 0
Your own private ChatGPT in hours? Azure Chat makes it possible! https://prodsens.live/2023/10/10/your-own-private-chatgpt-in-hours-azure-chat-makes-it-possible/?utm_source=rss&utm_medium=rss&utm_campaign=your-own-private-chatgpt-in-hours-azure-chat-makes-it-possible https://prodsens.live/2023/10/10/your-own-private-chatgpt-in-hours-azure-chat-makes-it-possible/#respond Tue, 10 Oct 2023 12:25:01 +0000 https://prodsens.live/2023/10/10/your-own-private-chatgpt-in-hours-azure-chat-makes-it-possible/ your-own-private-chatgpt-in-hours?-azure-chat-makes-it-possible!

I’ve been using ChatGPT Plus for many months now. Like many others, I use it for simple tasks…

The post Your own private ChatGPT in hours? Azure Chat makes it possible! appeared first on ProdSens.live.

]]>
your-own-private-chatgpt-in-hours?-azure-chat-makes-it-possible!

I’ve been using ChatGPT Plus for many months now. Like many others, I use it for simple tasks like spell-checking and more complex ones like brainstorming. It’s been great for my personal and work projects. But I wonder if the USD $20 per month fee is worth it for how often I use it. I only interact with ChatGPT a few times a week. If I used the OpenAI API, which charges as you go, I might only pay around $3 per month.

That’s one big reason I wanted to set up my own ChatGPT frontend. Not only would I pay for what I use, but I could also let my family use GPT-4 and keep our data private. My wife could finally experience the power of GPT-4 without us having to share a single account nor pay for multiple accounts.

It’s been a while since I did any serious web frontend work. I thought about increasing my Angular knowledge to make my own ChatGPT. I was ready for many late nights working on this. But then, in early August, I found this microsoft/azurechat on GitHub. Microsoft recently created this Azure Chat repository on July 11. Here’s a quote from their README:

Azure Chat Solution Accelerator powered by Azure Open AI Service is a solution accelerator that allows organisations to deploy a private chat tenant in their Azure Subscription, with a familiar user experience and the added capabilities of chatting over your data and files.

I tried it right away. In just 4 hours, I was able to set up my own private ChatGPT using Docker, Azure, and Cloudflare. The Azure Chat docs mostly talk about connecting with Azure OpenAI Service, and this service is currently in preview with limited access. Even though, I managed to connect it to the OpenAI API, which everyone can use. In this blog post, I’ll show you how to do the same.

A first look to Microsoft Azure Chat

Microsoft Azure Chat is a Next.js application. By default, NextAuth.js is configured to allow users to sign in with their Microsoft or GitHub account. The chat persistence layer is tightly coupled to Cosmos DB and LangChain is used to leverage the GPT models with Azure OpenAI.

If you plan to use Azure OpenAI, and you have access to the service in preview for your Azure subscription, then you can upload a PDF file and engage in chat discussions related to the content of those files.

There’s also a built-in chat reporting page that can be accessed by users whose email address is in a dedicated environment variable.

You must be an administrator to access the Azure Chat reporting page

Replacing Azure OpenAI with OpenAI API

Because Azure Chat uses LangChain, the built-in OpenAI integration uses environment variable detection to support both Azure OpenAI and OpenAI API. This means you can simply replace these suggested environment variables:

  • AZURE_OPENAI_API_KEY
  • AZURE_OPENAI_API_INSTANCE_NAME
  • AZURE_OPENAI_API_DEPLOYMENT_NAME
  • AZURE_OPENAI_API_VERSION

… with the single environment variable OPENAI_API_KEY which, as you might have guessed, must contain your API key for the OpenAI API.

Enabling the GPT-4 model in Azure Chat

By default, Azure Chat doesn’t specify which GPT model to use. This means that the default GPT-3.5 model is selected. If you want to leverage GPT-4, you’ll need to specify the model name when creating the ChatOpenAI instance in the backend:

const chat = new ChatOpenAI({
  temperature: transformConversationStyleToTemperature(
    chatThread.conversationStyle
  ),
  modelName: chatThread.chatModel, // <-- This is the new line
  streaming: true,
});

This model name must be provided by the frontend, so a few frontend and backend files need adjustment. To see the edits I made to my version, check out this pull request from my fork. In essence, you’ll be creating a chat model selector component for the chat UI and then sending the chosen model name to the backend.

Here’s how my chat UI appears with the GPT model selector:

Like ChatGPT, you cannot change the chat model once the conversation has started

With this, users have the choice to use either GPT-3 (gpt-3.5-turbo) or GPT-4 (gpt-4). You can also opt for any other GPT models available via the OpenAI API, such as gpt-4-32k which supports four times more tokens than the default GPT-4 OpenAI model.

Enabling other authentication providers

As I said earlier, Azure Chat supports Microsoft and GitHub authentication out of the box. However, because it’s using NextAuth.js, you can easily add other built-in authentication providers. In my case, I wanted my family to log in with their Google accounts. All I needed to do was to modify the auth-api.ts file and add the Google provider:

const configureIdentityProvider = () => {
  const providers: Array<Provider> = [];

  if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
    providers.push(
      GoogleProvider({
        clientId: process.env.GOOGLE_CLIENT_ID!,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        async profile(profile) {
          const newProfile = {
            ...profile,
            id: profile.sub,
            isAdmin: adminEmails.includes(profile.email.toLowerCase())
          }
          return newProfile;
        }
      })
    );
  }

  // ... rest of the code that configures Microsoft and GitHub providers

In the default configuration, all users from the configured providers can access the app. That’s why I added in my fork a mechanism to restrict access to a list of email addresses specified in an environment variable. It’s a simple implementation of NextAuth.js’s signIn‘s callback.

Deploying Azure Chat as a containerized application

Containerized applications can be deployed almost anywhere. I personally used Azure App Service with the free Cosmos DB tier in this scenario, but you could host it on premises, on a virtual machine or any cloud provider that supports containers.

Fortunately, the Dockerfile provided in the repository works right out of the box. You can use it as-is. I deploy my Azure Chat fork on Docker Hub using GitHub Actions with this workflow. Don’t forget to pass the environment variables to the container.

Next, I integrated Cloudflare as a reverse proxy for an extra layer of security, with a custom DNS entry.

Conclusion

Now, it’s your turn to fork microsoft/azurechat. While the documentation mentions enterprise use, I don’t think the project is quite ready for that scale, especially since Azure OpenAI isn’t generally available yet. However, at this stage, it’s excellent for personal use and it’s a cheaper replacement to ChatGPT Plus.

Do you find the “pay-as-you-go” model more appealing than a ChatGPT Plus subscription? Have any of you considered deploying Azure Chat for your customers? Do you know other notable “ChatGPT-like” frontends? For those who value privacy, would you prefer hosting your own ChatGPT? I invite you to share your thoughts in the comments!

The post Your own private ChatGPT in hours? Azure Chat makes it possible! appeared first on ProdSens.live.

]]>
https://prodsens.live/2023/10/10/your-own-private-chatgpt-in-hours-azure-chat-makes-it-possible/feed/ 0