Creating new assistant by calling a function

Hey - beginner here. I am trying to trigger the creation of an assistant when a specific page is rendered. The logic should be that when the page.tsx from first-page or second-page is compiled, then a specific assistant from assistant-config.ts is created and the corresponding assistantId is updated to .env.

I can’t seem to make it call the assistant function. Any ideas/advice? :slight_smile:

app\assistant-config.ts

import { openai } from "@/app/openai";
import { promises as fs } from 'fs';
import path from 'path';

// Use a dynamic import to handle ES modules if needed
const __dirname = path.dirname(new URL(import.meta.url).pathname);

export async function firstAssistant() {
  try {
    // Create the assistant
    const response = await openai.beta.assistants.create({
      name: "Haiku Poet",
      instructions: "You are a haiku poet.",
      tools: [{ type: "code_interpreter" }],
      model: "gpt-4o"
    });

    const assistantId = response.id; // Assuming response contains the assistant ID
    console.log("Assistant created with ID:", assistantId);

    // Define the relative path
    const envPath = path.resolve('.env');

    // Prepare the new line to update
    const newEnvLine = `assistantId="${assistantId}"`;

    // Read the existing .env file contents
    let envContent = await fs.readFile(envPath, 'utf8');

    // Update the ASSISTANT_ID line
    const updatedEnvContent = envContent.split('\n').map(line => {
      if (line.startsWith('assistantId=')) {
        return newEnvLine;
      }
      return line;
    }).join('\n');

    // Write the updated content back to the .env file
    await fs.writeFile(envPath, updatedEnvContent, 'utf8');
    console.log('Assistant ID updated in .env file.');
  } catch (error) {
    console.error('Error creating assistant or updating .env file:', error);
  }
}

export async function secondAssistant() {
  try {
    // Create the assistant
    const response = await openai.beta.assistants.create({
      name: "Haiku Poet",
      instructions: "You are a haiku poet.",
      tools: [{ type: "code_interpreter" }],
      model: "gpt-4o"
    });

    const assistantId = response.id; // Assuming response contains the assistant ID
    console.log("Assistant created with ID:", assistantId);

    // Define the relative path
    const envPath = path.resolve('.env');

    // Prepare the new line to update
    const newEnvLine = `assistantId="${assistantId}"`;

    // Read the existing .env file contents
    let envContent = await fs.readFile(envPath, 'utf8');

    // Update the ASSISTANT_ID line
    const updatedEnvContent = envContent.split('\n').map(line => {
      if (line.startsWith('assistantId=')) {
        return newEnvLine;
      }
      return line;
    }).join('\n');

    // Write the updated content back to the .env file
    await fs.writeFile(envPath, updatedEnvContent, 'utf8');
    console.log('Assistant ID updated in .env file.');
  } catch (error) {
    console.error('Error creating assistant or updating .env file:', error);
  }
}

app\examples\first-chat\page.tsx

"use client";

import React from "react";
import styles from "./page.module.css"; // use simple styles for demonstration purposes
import Chat from "../../components/chat";
import { firstAssistant } from "../../assistant-config";

// Call the function to create the assistant and handle any errors
firstAssistant().catch(console.error);

const Home = () => {
  return (
    <main className={styles.main}>
      <div className={styles.container}>
        <Chat />
      </div>
    </main>
  );
};

export default Home;

app\examples\second-chat\page.tsx

"use client";

import React from "react";
import styles from "./page.module.css"; // use simple styles for demonstration purposes
import Chat from "../../components/chat";
import { secondAssistant } from "../../assistant-config";

// Call the function to create the assistant and handle any errors
secondAssistant().catch(console.error);

const Home = () => {
  return (
    <main className={styles.main}>
      <div className={styles.container}>
        <Chat />
      </div>
    </main>
  );
};

export default Home;

why are you dynamically creating assistants? your sample show same instructions, is this by design or not? there is no issue in creating assistants dynamically but perhaps there is a better way to achieve what you want.

1 Like

Hi, thank you for replying!

The reasoning for this is to have multiple assistant. Then when a specific page is rendered it will trigger the creation of an assistant and saves the assistantId.

In the file route.ts, the assistantId is taken from .env. I changed it to that from importing it via assistant-config.ts, as you can see in the first entry of this post. The reason for this, was that the thread would create a NEW assistant for every message, which is not wanted. I want only the assistant to be created once, which is why I sorted to initalizing assitant-config.ts and saving the assistantId to .env, as described in the first entry. Since I have multiple assistants, I want the assistantId to be overwritten only in the case of a new assistant - not a new message.

Do you have any ideas?

app\api\assistants\threads[threadId]\messages\route.ts

import { openai } from "@/app/openai";
let assistantId = process.env.assistantId;

export const runtime = "nodejs";

// Send a new message to a thread
export async function POST(request, { params: { threadId } }) {
  const { content } = await request.json();

  await openai.beta.threads.messages.create(threadId, {
    role: "user",
    content: content,
  });

  const stream = openai.beta.threads.runs.stream(threadId, {
    assistant_id: assistantId,
  });

  return new Response(stream.toReadableStream());
}

I have not reviewed your code but since you mentionned you were a beginner, it is possible you don’t completely get the assistant.
You can create 1 assistant for all your session (threads) and reuse the same one every time. Each assistant only has access to the “present” conversation (thread). If you have many users for example, you can start the conversation with a user message upon creation of the the thread. The message could introduce the user and other things such as “if requested to write an email put it in a “pre” tag and keep it short” or “you are an assistant for this “page” of our electronic buying website…”. In my case I also tell the assistant how many threads this particuar user created and the assistant answer something (with variation of course) like “Hi Johanne how can I help you today?”.
Hope this helps.

1 Like

Thanks for your reply!

Your suggestion might work. Do you mind sharing how you differentiate between the different messages in the thread?

The reason for the multiple assistants is due to the different instructions. So each assistant is configured differently. Perhaps that can be solved by defining what message/instruction to use directly in the thread, if that is what you meant

Ok, here is a line of code, when I add a message to the thread:

message = oc.openai.beta.threads.messages.create(thread_id=data.get(‘thread_id’), content=data.get(‘message’), role=data.get(‘role’))

Once the message is added, I create a “run”, which in effect “submit it” for evaluation.

run = oc.openai.beta.threads.runs.create(thread_id = data.get(‘thread_id’), assistant_id= assistant_id)

that run variable gets a run.id

Then, in my chatbox, I have a setInterval on my browser page that (thru the server) retrieve the run back:

oc.openai.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run_id)

then, in my case I check the last message on the thread; if it’s different than the last message.id then Iknow I’ve gotten an answer.
To explain: in my back and forth between the client and the server I shuttle the message but also the latest message id so I know which one was the last one.
If there is no new message, then there’s two possibilities: either the run status is queued or in_progress (in which case I return and restart on the next call of my setInterval) or the status is require_action; that occurs when you setup the assistant to call functions (that’s a little more involved).

messages = oc.openai.beta.threads.messages.list(thread_id)
message = messages.data[0]
#no matter what is the status, if there’s a new message, we return it
if message.id != last_message_id and len(messages.data) > 1: #the first message is the one autogenerated on opening the thread

elif run.status in [‘in_progress’, ‘queued’]:

elif run.status == ‘requires_action’:

About your second point, even if you setup a default instructions, you can override it by sending a new one as create the thread or send your (first and subsequent???) messages. (check in the documentation as I don’t use it myself) It will completely override the default one, it’s not an addtition.

Hi,

It looks like you’re trying to use Assistants like Completions.

Completions are sort-of like you just want to talk to chat-gpt. The message disappears when the conversation is over. Their inherently stateless.

But Assistants are designed to persist between conversations. The idea is you design them independent of your code. You can create Assistants programmatically, but I personally think it makes more sense in the platform; but I don’t think you’d want Assistant creation in your loop (or even in the same script).

So go design all of the different Assistants you need. Then take their id’s and put them in .env for calling in your script.

If it helps, I created a video explaining whole OpenAI Assistant APIs V2. Let me know if you have any specific questions.