Skip to content

Commit

Permalink
Merge branch 'main' of github.com:oramasearch/javascript-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
micheleriva committed Apr 25, 2024
2 parents 38489df + b16df75 commit 71e5847
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 60 deletions.
3 changes: 3 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"enabled": true,
"rules": {
"recommended": true,
"style": {
"noNonNullAssertion": "off"
},
"suspicious": {
"noExplicitAny": "off"
}
Expand Down
67 changes: 44 additions & 23 deletions src/answerSession.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Results, AnyDocument } from '@orama/orama'
import type { Results, AnyDocument, SearchParams, AnyOrama } from '@orama/orama'
import { OramaClient } from './client.js'
import { EventEmitter } from './eventEmitter.js'

export type Context = Results<AnyDocument>['hits']

Expand All @@ -9,59 +8,66 @@ export type Message = {
content: string
}

export type Mode = 'fulltext' | 'vector' | 'hybrid'

export type InferenceType = 'documentation'

type AnswerParams = {
mode: Mode
initialMessages: Message[]
inferenceType: InferenceType
oramaClient: OramaClient
events?: {
onMessageChange?: (messages: Message[]) => void
onMessageLoading?: (receivingMessage: boolean) => void
onAnswerAborted?: (aborted: true) => void
onSourceChange?: <T = AnyDocument>(sources: Results<T>) => void
}
}

export class AnswerSession extends EventEmitter {
export class AnswerSession {
private messages: Message[]
private mode: Mode = 'fulltext'
private inferenceType: InferenceType
private oramaClient: OramaClient
private endpoint: string
private abortController?: AbortController
private events: AnswerParams['events']

constructor(params: AnswerParams) {
super()
this.messages = params.initialMessages || []
this.mode = params.mode
this.inferenceType = params.inferenceType
this.oramaClient = params.oramaClient
// @ts-expect-error - sorry TypeScript
this.endpoint = `${this.oramaClient.endpoint}/answer?api-key=${this.oramaClient.api_key}`
this.events = params.events
}

public askStream(question: string, context: Context): AsyncGenerator<string> {
this.messages.push({ role: 'user', content: question })
return this.fetchAnswer(question, context)
public async askStream(params: SearchParams<AnyOrama>): Promise<AsyncGenerator<string>> {
this.messages.push({ role: 'user', content: params.term ?? '' })
const inferenceResult = await this.runInference(params)

if (this.events?.onSourceChange) {
this.events.onSourceChange(inferenceResult!)
}

return this.fetchAnswer(params.term ?? '', inferenceResult?.hits ?? [])
}

public async ask(question: string, context: Context): Promise<string> {
const generator = this.askStream(question, context)
public async ask(params: SearchParams<AnyOrama>): Promise<string> {
const generator = await this.askStream(params)
let result = ''
for await (const message of generator) {
result = message
}

this.emit('message-change', this.messages)
if (this.events?.onMessageChange) {
this.events.onMessageChange(this.messages)
}

return result
}

public getMessages(): Message[] {
return this.messages
}

public setMode(mode: Mode): void {
this.mode = mode
}

public clearSession(): void {
this.messages = []
}
Expand All @@ -78,6 +84,10 @@ export class AnswerSession extends EventEmitter {
}
}

private runInference(params: SearchParams<AnyOrama>) {
return this.oramaClient.search(params)
}

private async *fetchAnswer(query: string, context: Context): AsyncGenerator<string> {
this.abortController = new AbortController()
const { signal } = this.abortController
Expand Down Expand Up @@ -105,7 +115,10 @@ export class AnswerSession extends EventEmitter {
const reader = response.body.getReader()
const decoder = new TextDecoder()

this.emit('message-loading', true)
if (this.events?.onMessageLoading) {
this.events.onMessageLoading(true)
}

this.addNewEmptyAssistantMessage()

const lastMessage = this.messages.at(-1) as Message
Expand All @@ -118,17 +131,25 @@ export class AnswerSession extends EventEmitter {
}
const decodedChunk = decoder.decode(value, { stream: true })
lastMessage.content += decodedChunk
this.emit('message-change', this.messages)

if (this.events?.onMessageChange) {
this.events.onMessageChange(this.messages)
}

yield lastMessage.content
}
} catch (err) {
if ((err as any).name === 'AbortError') {
this.emit('answer-aborted', true)
if (this.events?.onAnswerAborted) {
this.events.onAnswerAborted(true)
}
} else {
throw err
}
}

this.emit('message-loading', false)
if (this.events?.onMessageLoading) {
this.events.onMessageLoading(false)
}
}
}
19 changes: 11 additions & 8 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Endpoint, IOramaClient, Method, OramaInitResponse, HeartBeatConfig, OramaError } from './types.js'
import type { SearchParams, Results, AnyDocument, AnyOrama, Nullable } from '@orama/orama'
import type { Message, Mode, InferenceType } from './answerSession.js'
import type { Message, InferenceType } from './answerSession.js'
import { formatElapsedTime } from '@orama/orama/components'
import { createId } from '@paralleldrive/cuid2'

Expand Down Expand Up @@ -28,16 +28,20 @@ export type AnswerParams = {
type: 'documentation'
query: string
messages: Array<{ role: 'user' | 'system'; content: string }>
// biome-ignore lint/suspicious/noExplicitAny: keep any for now
context: Results<any>['hits']
}

export type ClientSearchParams = SearchParams<AnyOrama> & AdditionalSearchParams

export type AnswerSessionParams = {
inferenceType: InferenceType
initialMessages: Message[]
mode: Mode
inferenceType?: InferenceType
initialMessages?: Message[]
events?: {
onMessageChange?: (messages: Message[]) => void
onMessageLoading?: (receivingMessage: boolean) => void
onAnswerAborted?: (aborted: true) => void
onSourceChange?: <T = AnyDocument>(sources: Results<T>) => void
}
}

export class OramaClient {
Expand Down Expand Up @@ -206,8 +210,8 @@ export class OramaClient {
return new AnswerSession({
inferenceType: params?.inferenceType || 'documentation',
initialMessages: params?.initialMessages || [],
mode: params?.mode || 'fulltext',
oramaClient: this
oramaClient: this,
events: params?.events
})
}

Expand Down Expand Up @@ -263,7 +267,6 @@ export class OramaClient {
}

if (method === 'POST' && body !== undefined) {
// biome-ignore lint/suspicious/noExplicitAny: keep any for now
const b = body as any
b.version = version
b.id = this.id
Expand Down
29 changes: 0 additions & 29 deletions src/eventEmitter.ts

This file was deleted.

0 comments on commit 71e5847

Please sign in to comment.