Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions browser-extension/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AgentSession } from "./scripts/agent-session";
import { findAgentSession, saveAgentSession, removeAgentSession, } from "./scripts/agent-session-repository";
import { saveAgentPrompts } from "./scripts/prompt-repository";
import { BrowserMessage, ToggleSidebar, ActiveTabListener, ActivateAgent, AgentActivation, InteractionSummary, } from "./scripts/browser-message";
import { HttpServiceError } from "./scripts/http";
import { BaseError } from "./scripts/http";
import { isActiveTabListener, setTabListenerActive, removeTabListenerStatus, } from "./scripts/tab-listener-status-repository";
import { removeTabState } from "./scripts/tab-state-repository";

Expand Down Expand Up @@ -174,8 +174,9 @@ async function asyncProcessRequest(req: RequestEvent) {
sendToTab(
tabId,
new InteractionSummary(
false,
e instanceof HttpServiceError ? e.detail : undefined
false,
e instanceof BaseError ? e.detail : undefined,
e instanceof BaseError ? e.getType() : undefined
)
);
}
Expand Down
13 changes: 11 additions & 2 deletions browser-extension/src/pages/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,14 @@ const onAgentActivation = (msg: AgentActivation) => {
}

const onInteractionSummary = (msg: InteractionSummary) => {
const text = msg.text ? msg.text : t('interactionSummaryError', { contactEmail: agent.value!.manifest.contactEmail })
let text = msg.text ?? ''

if (!msg.success) {
text = msg.errorType === 'NetworkError' ? t('networkError') : t('interactionSummaryError', {
contactEmail: agent.value!.manifest.contactEmail
})
}

const lastMessage = messages.value[messages.value.length - 1]
const messagePosition = lastMessage.isComplete ? messages.value.length : messages.value.length - 1
messages.value.splice(messagePosition, 0, msg.success ? ChatMessage.agentMessage(text) : ChatMessage.agentErrorMessage(text))
Expand Down Expand Up @@ -257,12 +264,14 @@ const sidebarClasses = computed(() => [
"interactionSummaryError": "I could not process some information from the current site. This might impact the information and answers I provide. If the issue persists please contact [support](mailto:{contactEmail}?subject=Interaction%20issue)",
"agentAnswerError": "I am currently unable to complete your request. You can try again and if the issue persists contact [support](mailto:{contactEmail}?subject=Question%20issue)",
"flowStepMissingElement": "I could not find the element '{selector}'. This might be due to recent changes in the page which I am not aware of. Please try again and if the issue persists contact [support](mailto:{contactEmail}?subject=Navigation%20element).",
"networkError": "It seems there is a problem with the network connection. Please check your connection and try again."
},
"es": {
"activationError": "No se pudo activar el Copiloto {agentName}. Puedes intentar de nuevo y si el problema persiste contactar al [soporte del Copiloto {agentName}](mailto:{contactEmail}?subject=Activation%20issue)",
"interactionSummaryError": "No pude procesar informacion generada por la página actual. Esto puede impactar en la información y respuestas que te puedo dar. Si el problema persiste por favor contacta a [soporte](mailto:{contactEmail})?subject=Interaction%20issue",
"agentAnswerError": "Ahora no puedo completar tu pedido. Puedes intentar de nuevo y si el problema persiste contactar a [soporte](mailto:{contactEmail}?subject=Question%20issue)",
"flowStepMissingElement": "No pude encontrar el elemento '{selector}'. Esto puede ser debido a cambios recientes en la página de los cuales no tengo conocimiento. Por favor intenta de nuevo y si el problema persiste contacta a [soporte](mailto:{contactEmail}?subject=Navigation%20element).",
"flowStepMissingElement": "No pude encontrar el elemento '{selector}'. Esto puede ser debido a cambios recientes en la página de los cuales no tengo conocimiento. Por favor intenta de nuevo y si el problema persiste contacta a [soporte](mailto:{contactEmail}?subject=Navigation%20element).",
"networkError": "Parece que hay un problema con la conexión a la red. Por favor verifica tu conexión y vuelve a intentarlo."
}
}
</i18n>
23 changes: 21 additions & 2 deletions browser-extension/src/scripts/agent-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class AgentSession {

private async pollInteraction(msgSender: (msg: BrowserMessage) => void) {
try {
const summary = await this.agent.solveInteractionSummary(undefined, this.id!, this.authService)
const summary = await this.retryInteractionSummary(undefined, this.id!, this.authService)
if (summary) {
await msgSender(new InteractionSummary(true, summary))
}
Expand All @@ -120,7 +120,7 @@ export class AgentSession {
for (const a of actions) {
if (a.recordInteraction) {
const interactionDetail = await this.findInteraction(req, a.recordInteraction)
return await this.agent.solveInteractionSummary(interactionDetail, this.id!, this.authService)
return await this.retryInteractionSummary(interactionDetail, this.id!, this.authService)
}
}
}
Expand Down Expand Up @@ -189,4 +189,23 @@ export class AgentSession {
}
}

private async retryInteractionSummary(interactionDetail: any | undefined, sessionId: string, authService?: AuthService, maxRetries = 2, delayMs = 5000): Promise<string | undefined> {
let attempts = 0
let lastError: any

while (attempts <= maxRetries) {
try {
return await this.agent.solveInteractionSummary(interactionDetail, sessionId, authService)
} catch (error) {
lastError = error
attempts++

if (attempts <= maxRetries) {
await new Promise(resolve => setTimeout(resolve, delayMs))
}
}
}

throw lastError
}
}
6 changes: 4 additions & 2 deletions browser-extension/src/scripts/browser-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,17 @@ export class AgentActivation extends BrowserMessage {
export class InteractionSummary extends BrowserMessage {
text?: string
success: boolean
errorType?: string

constructor(success: boolean, text?: string) {
constructor(success: boolean, text?: string, errorType?: string) {
super("interactionSummary")
this.text = text
this.success = success
this.errorType = errorType
}

public static fromJsonObject(obj: any): InteractionSummary {
return new InteractionSummary(obj.success, obj.text)
return new InteractionSummary(obj.success, obj.text, obj.errorType)
}
}

Expand Down
30 changes: 28 additions & 2 deletions browser-extension/src/scripts/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@ export const fetchJson = async (url: string, options?: RequestInit) => {
}

const fetchResponse = async (url: string, options?: RequestInit) => {
let ret = await fetch(url, options)
let ret

try {
ret = await fetch(url, options)
} catch (error) {
// This handles the case where the user is temporarily disconnected from the internet
const partialErrorMessage = "Failed to fetch"

if (error instanceof TypeError && error.message.includes(partialErrorMessage)) {
throw new NetworkError(partialErrorMessage)
}

throw error
}

if (ret.status < 200 || ret.status >= 300) {
let body = await ret.text()
console.warn(`Problem with ${options?.method ? options.method : 'GET'} ${url}`, { status: ret.status, body: body })
Expand All @@ -19,14 +33,26 @@ const fetchResponse = async (url: string, options?: RequestInit) => {
return ret
}

export class HttpServiceError extends Error {
export class BaseError extends Error {
detail?: string
readonly type: string = 'BaseError'

constructor(detail?: string) {
super()
this.detail = detail
}

getType(): string {
return this.type
}
}

export class HttpServiceError extends BaseError {
readonly type: string = 'HttpServiceError'
}

export class NetworkError extends BaseError {
readonly type: string = 'NetworkError'
}

export async function* fetchStreamJson(url: string, options?: RequestInit): AsyncIterable<any> {
Expand Down