Skip to content

Creating an Answer Session

Once you have set up your index, you will be able to engage in dynamic conversations with it.

The Orama Cloud SDK exposes a very convenient function for performing highly dynamic generative AI experiences with your data through the answerSession API.

Let’s see what it looks like.

Getting Started

Follow the instructions here to install the official JavaScript SDK. After you’ve installed it, you will be able to create your first answer session:

import { OramaClient } from "@oramacloud/client";
const orama = new OramaClient({
endpoint: "YOUR_ENDPOINT_URL",
api_key: "YOUR_PUBLIC_API_KEY",
});
const answerSession = orama.createAnswerSession({
// optional
userContext: "The user is a very skilled programmer but has never used Orama before.",
// optional
inferenceType: "documentation",
// optional
initialMessages: [],
// optional
events: {
onMessageChange: (messages) => console.log({ messages }),
onMessageLoading: (loading) => console.log({ loading }),
onAnswerAborted: (aborted) => console.log({ aborted }),
onSourceChange: (sources) => console.log({ sources }),
onQueryTranslated: (query) => console.log({ query }),
onStateChange: (state) => console.log({ state }),
onNewInteractionStarted: (interactionId) => console.log({ interactionId }),
},
});
await answerSession.ask({
term: "How do I get started with Orama?",
});

All the parameters above are optional, but you may need to use them to create great answer sessions for your users. Let’s see them in detail.

userContext

userContext is a string or an object that describes the user’s context. This is useful when you want to provide a more personalized experience to your users. For example, if you know that the user is a beginner in programming, you can provide a more detailed explanation of the concepts you’re talking about. This is optional, but it’s a good practice to provide it.

If we pretend to be an online music store, and we know who the logged in user is, we could provide the following user context:

const answerSession = orama.createAnswerSession({
userContext: 'The user is called John Doe and he loves rock music. He has been a customer for 5 years.'
})

If we prefer, we can also use an object to provide a more structured context:

const answerSession = orama.createAnswerSession({
userContext: {
name: 'John Doe',
musicTaste: ['Rock', 'Punk', 'Metal'],
customerSince: 2019
}
})

inferenceType

inferenceType refers to the type of inference that runs on your data. Right now, only documentation is supported - therefore it’s set as a default value - but we’ll enable more inference types soon (website, blog, ecommerce, generic, and more)

initialMessages

An array of initial messages for your conversational experience with Orama Cloud. By default, this parameter is an empty array, but it can be an array of objects in the following format:

const initialMessages = [
{ role: "user", content: "What is Orama?" },
{ role: "assistant", content: "Orama is a next-generation answer engine" },
];

events

The events to subscribe to. You will get notified whenever a new event gets triggered. More on this in the next section.

Session Events

Answers are a highly dynamic part of Orama. They work in an asynchronous manner and requires you to subscribe to events to catch any new change in the answer flow and react to it.

At the time of writing, Orama supports the following events:

onStateChange

Runs everytime the state of the answer session changes. In this context, the state refers to a list of interactions between the user and Orama Cloud.

Each interaction looks like this:

import type { AnyDocument, AnyOrama, Nullable, Results, SearchParams } from "@oramacloud/client";
export type Interaction<T = AnyDocument> = {
interactionId: string,
query: string,
response: string,
relatedQueries: Nullable<string[]>,
sources: Nullable<Results<T>>,
translatedQuery: Nullable<SearchParams<AnyOrama>>,
aborted: boolean,
loading: boolean
}

Consequently, the onStateChange event will return an array of these interactions:

const answerSession = orama.createAnswerSession({
events: {
onStateChange: (state) => {
if (state.every(interaction => !interaction.loading)) {
console.log(state)
}
},
},
});
await answerSession.ask({
term: "What is Orama?",
});
// [
// {
// interactionId: "clyru4rl8000008l062b26fk1",
// query: "What is Orama?",
// response: "Orama is a next-generation answer engine [...]",
// relatedQueries: ["How Orama works", "Why Orama is the best", "Vector search with orama"],
// sources: [
// {
// count: 15,
// elapsed: { formatted: "78ms", raw: 78000000 },
// hits: [
// { document: { title: "What is Orama", ... } },
// { document: { title: "How Orama works", ... } },
// { document: { title: "Why Orama is the best", ... } }
// ]
// }
// ],
// translatedQuery: { term: "What is Orama?" },
// aborted: false,
// loading: false
// }
// ]

The onStateChange event gets triggered everytime there’s a change in the state, which means that it will also get triggered when the server streams a new chunk of the answer back to the client.

This is particularly useful when creating reactive interfaces that need to update the UI as the answer gets streamed back from the server with frameworks like React, Vue, or Angular.

You can always access a static version of the state by using the state getter:

const answerSession = orama.createAnswerSession();
await answerSession.ask({
term: "What is Orama?",
});
console.log(answerSession.state);
// [
// {
// interactionId: "clyru4rl8000008l062b26fk1",
// query: "What is Orama?",
// response: "Orama is a next-generation answer engine [...]",
// relatedQueries: ["How Orama works", "Why Orama is the best", "Vector search with orama"],
// sources: [
// {
// count: 15,
// elapsed: { formatted: "78ms", raw: 78000000 },
// hits: [
// { document: { title: "What is Orama", ... } },
// { document: { title: "How Orama works", ... } },
// { document: { title: "Why Orama is the best", ... } }
// ]
// }
// ],
// translatedQuery: { term: "What is Orama?" },
// aborted: false,
// loading: false
// }
// ]

onMessageChange

Runs everytime a message changes. This gets triggered with each chunk that gets streamed from the server, allowing you to re-render the messages in your page as they gets updated.

const answerSession = orama.createAnswerSession({
events: {
onMessageChange: (messages) => {
console.log(messages);
// [
// { role: 'user', content: 'What is Orama?' },
// { role: 'assistant', content: 'Orama is a next-generation answer engine' }
// ]
},
},
});
await answerSession.ask({
term: "What is Orama?",
});

onMessageLoading

Gets triggered everytime an answer request starts or ends.

const answerSession = orama.createAnswerSession({
events: {
onMessageLoading: (loading) => {
if (loading) {
console.log("Still loading the messages...");
}
},
},
});
await answerSession.ask({
term: "What is Orama?",
});

onAnswerAborted

Gets triggered when an answer gets aborted.

const answerSession = orama.createAnswerSession({
events: {
onAnswerAborted: (aborted) => {
alert("The user has aborted this answer generation!");
},
},
});
await answerSession.ask({
term: "What is Orama?",
});
setTimeout(() => {
answerSession.abortAnswer();
}, 1000);

onSourceChange

Orama Cloud will return the results of the inference process as a standard set of Orama search results. You will have access to those as soon as they gets computed:

const answerSession = orama.createAnswerSession({
events: {
onSourceChange: (sources) => {
console.log(
`Got ${sources.count} sources in ${sources.elapsed.formatted}.`
);
console.log(
`Top three sources are: ${sources.hits
.map((hit) => hit.document.title)
.join(", ")}`
);
// Got 15 sources in 78ms.
// Top three sources are: What is Orama, How Orama works, Why Orama is the best
},
},
});
await answerSession.ask({
term: "What is Orama?",
});

onQueryTranslated

Gets triggered when Orama successfully translates a natural language query into a search query. This is useful when you want to know how Orama interprets the user’s input. It adapts to your index schema and returns the most relevant search query.

const answerSession = orama.createAnswerSession({
events: {
onQueryTranslated: (query) => {
console.log(query);
// { term: 'Halfpipe skateboard', where: { price: { lt: 100 } } }
},
},
});
await answerSession.ask({
term: "What's the best skateboard for halfpipes under $100?",
});

Asking a Question

With the Orama JavaScript SDK, you can ask questions in two different ways:

  1. using the .ask function
  2. using the .askStream function

Both methods accept the search parameters you already use to perform search, so you can influence the inference process by filtering, sorting, and grouping the results as you prefer!

They both trigger the exact same events, but the only difference is how they return the answer.

The .ask function will return the entire answer once it has been entirely streamed from the server, therefore, you can use this method as follows:

const answer = await answerSession.ask({
term: "What is Orama?",
});
console.log(answer);
// Orama is a next-generation answer engine

The .askStream method returns an async iterator that yields chunks of the answer as they gets streamed back from the server. Therefore, this is how you would implement it:

const answer = await answerSession.askStream({
term: "What is Orama?",
});
for await (const msg of answer) {
console.log(msg);
}
// Orama
// is a
// next-gener
// ation answer
// engine

Aborting Sessions

In any moment in time (when a user cancels an answer request, for example) you can abort an answer by using the .abortAnswer function:

answerSession.abortAnswer();

This will trigger the onAnswerAborted event, which will simply return a true in its callback function:

const answerSession = orama.createAnswerSession({
events: {
onAnswerAborted: (aborted) => {
alert("The user aborted the answer session!");
},
},
});

When creating answer sessions, you may want to provide your users with a list of related queries that they can use to get more information about a specific topic. You can do this by using the related parameter:

When asking for "question" (default) related queries, Orama will return an array of related queries in a question format. For example, if the user asks for: “What is Orama?”, Orama will return an array of related queries like: ["How does Orama work?", "Why is Orama the best?", "How do I run vector search?"].

If the format is "query", Orama will return an array of related queries in a query format. For example, if the user asks for: “What is Orama?”, Orama will return an array of related queries like: ["How Orama works", "Why Orama is the best", "Vector search with Orama"].

const answerSession = orama.createAnswerSession({
events: {
onRelatedQueries: (relatedQueries) => {
console.log(relatedQueries);
// ["How Orama works", "Why Orama is the best", "Vector search with Orama"]
},
}
});
await answerSession.ask({
term: "What is Orama?",
related: {
howMany: 3, // How many related queries you want to get. Maximum is 5.
format: 'query'
}
});

Getting all the messages

At any point in time, you can retrieve all the messages by calling the .getMessages function:

const messages = answerSession.getMessages();
console.log(messages);
// [
// { role: 'user', content: 'What is Orama?' },
// { role: 'assistant', content: 'Orama is a next-generation answer engine' }
// ]

Regenerating the last answer

Sometimes, the user may want to regenerate the last answer. You can do this by calling the .regenerateLast method:

// Ask the question
await answerSession.ask({ term: "What is Orama?" });
// Regenerate the answer
// "stream" is true by default, but you can set it to false if you want to get the entire answer at once
await answerSession.regenerateLast({ stream: false });
// Check the messages
const messages = answerSession.getMessages();
console.log(messages);
// [
// { role: 'user', content: 'What is Orama?' },
// { role: 'assistant', content: 'Orama is a next-generation answer engine' }
// ]

Clearing the Session

You can clear the answer session by using the clearSession method. This will reset the messages to an empty array:

answerSession.clearSession();