fix(otto): prevent breaking when failing to make api call (#9613)

These are some tweaks for Otto to try prevent the frontend from breaking
when/if it fails to make the otto api call.

### Changes 🏗️
Removed unused reference to ``OTTO_API_URL`` in
[backend/server/v2/otto/routes.py](https://github.com/Significant-Gravitas/AutoGPT/compare/dev...otto-fixes?expand=1#diff-6545b2ed01d619cc095b8d0ca6d5baa86d1448dc970cff508669ec3430675d28)
Removed un-needed ``revalidatePath("/build");`` from
[frontend/src/app/build/actions.ts](https://github.com/Significant-Gravitas/AutoGPT/compare/dev...otto-fixes?expand=1#diff-6861f6b90ce91138b3821c0a82abfffbc09f425c26b7335fac60f54894e353e9)
Added a 60 second timeout to the api call in
[backend/server/v2/otto/service.py](https://github.com/Significant-Gravitas/AutoGPT/compare/dev...otto-fixes?expand=1#diff-63c9a1a5337cd5e4ddec7544a258916d4998a6cb5c4181f621d7e24f654bd5c8)
Added a better error handler in
[frontend/src/components/OttoChatWidget.tsx](https://github.com/Significant-Gravitas/AutoGPT/compare/dev...otto-fixes?expand=1#diff-7351568d5c588b77f35d80994ca6800a7faa1b3b0ca229970cfa491eab4b4b33)
Made it so errors return a structured error response for better handling
in
[frontend/src/app/build/actions.ts](https://github.com/Significant-Gravitas/AutoGPT/compare/dev...otto-fixes?expand=1#diff-6861f6b90ce91138b3821c0a82abfffbc09f425c26b7335fac60f54894e353e9)

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  <!-- Put your test plan here: -->
  - [x] do not set the ENV var for ``otto_api_url`` 
- [x] Set the ENV var for ``NEXT_PUBLIC_BEHAVE_AS`` to ``CLOUD`` to have
the chat ui show in the build page
- [x] Send otto a message from the build page frontend, it should fail
and not break the frontend
- [x] Send otto a message that contains graph data to see if the sending
graph data works, this should also fail and not break the frontend
  - [x] now we set the ENV for ``otto_api_url``
  - [x] Send otto a message from the build page frontend, it should work
- [x] Send otto a message that contains graph data to see if the sending
graph data works, it should work
This commit is contained in:
Bently
2025-03-12 07:36:55 +00:00
committed by GitHub
parent c179a49218
commit c1e329497c
5 changed files with 81 additions and 54 deletions

View File

@@ -4,15 +4,11 @@ from autogpt_libs.auth.middleware import auth_middleware
from fastapi import APIRouter, Depends
from backend.server.utils import get_user_id
from backend.util.settings import Settings
from .models import ApiResponse, ChatRequest
from .service import OttoService
logger = logging.getLogger(__name__)
settings = Settings()
OTTO_API_URL = settings.config.otto_api_url
router = APIRouter()

View File

@@ -1,3 +1,4 @@
import asyncio
import logging
from typing import Optional
@@ -67,6 +68,13 @@ class OttoService:
"""
Send request to Otto API and handle the response.
"""
# Check if Otto API URL is configured
if not OTTO_API_URL:
logger.error("Otto API URL is not configured")
raise HTTPException(
status_code=503, detail="Otto service is not configured"
)
try:
async with aiohttp.ClientSession() as session:
headers = {
@@ -94,7 +102,10 @@ class OttoService:
logger.debug(f"Request payload: {payload}")
async with session.post(
OTTO_API_URL, json=payload, headers=headers
OTTO_API_URL,
json=payload,
headers=headers,
timeout=aiohttp.ClientTimeout(total=60),
) as response:
if response.status != 200:
error_text = await response.text()
@@ -115,6 +126,11 @@ class OttoService:
raise HTTPException(
status_code=503, detail="Failed to connect to Otto service"
)
except asyncio.TimeoutError:
logger.error("Timeout error connecting to Otto API after 60 seconds")
raise HTTPException(
status_code=504, detail="Request to Otto service timed out"
)
except Exception as e:
logger.error(f"Unexpected error in Otto API proxy: {str(e)}")
raise HTTPException(

View File

@@ -24,10 +24,14 @@ export async function askOtto(
try {
const response = await api.askOtto(ottoQuery);
revalidatePath("/build");
return response;
} catch (error) {
console.error("Error in askOtto server action:", error);
throw error;
return {
answer: error instanceof Error ? error.message : "Unknown error occurred",
documents: [],
success: false,
error: true,
};
}
}

View File

@@ -56,29 +56,30 @@ const OttoChatWidget = () => {
// Add user message to chat
setMessages((prev) => [...prev, { type: "user", content: userMessage }]);
// Add temporary processing message
setMessages((prev) => [
...prev,
{ type: "assistant", content: "Processing your question..." },
]);
const conversationHistory = messages.reduce<
{ query: string; response: string }[]
>((acc, msg, i, arr) => {
if (
msg.type === "user" &&
i + 1 < arr.length &&
arr[i + 1].type === "assistant" &&
arr[i + 1].content !== "Processing your question..."
) {
acc.push({
query: msg.content,
response: arr[i + 1].content,
});
}
return acc;
}, []);
try {
// Add temporary processing message
setMessages((prev) => [
...prev,
{ type: "assistant", content: "Processing your question..." },
]);
const conversationHistory = messages.reduce<
{ query: string; response: string }[]
>((acc, msg, i, arr) => {
if (
msg.type === "user" &&
i + 1 < arr.length &&
arr[i + 1].type === "assistant"
) {
acc.push({
query: msg.content,
response: arr[i + 1].content,
});
}
return acc;
}, []);
const data = await askOtto(
userMessage,
conversationHistory,
@@ -86,34 +87,43 @@ const OttoChatWidget = () => {
flowID || undefined,
);
// Remove processing message and add actual response
setMessages((prev) => [
...prev.slice(0, -1),
{ type: "assistant", content: data.answer },
]);
} catch (error) {
console.error("Error calling API:", error);
// Remove processing message and add error message
const errorMessage =
error instanceof Error && error.message === "Authentication required"
? "Please sign in to use the chat feature."
: "Sorry, there was an error processing your message. Please try again.";
// Check if the response contains an error
if ("error" in data && data.error === true) {
// Handle different error types
let errorMessage =
"Sorry, there was an error processing your message. Please try again.";
setMessages((prev) => [
...prev.slice(0, -1),
{ type: "assistant", content: errorMessage },
]);
if (data.answer === "Authentication required") {
errorMessage = "Please sign in to use the chat feature.";
} else if (data.answer === "Failed to connect to Otto service") {
errorMessage =
"Otto service is currently unavailable. Please try again later.";
} else if (data.answer.includes("timed out")) {
errorMessage = "Request timed out. Please try again later.";
}
if (
error instanceof Error &&
error.message === "Authentication required"
) {
toast({
title: "Authentication Error",
description: "Please sign in to use the chat feature.",
variant: "destructive",
});
// Remove processing message and add error message
setMessages((prev) => [
...prev.slice(0, -1),
{ type: "assistant", content: errorMessage },
]);
} else {
// Remove processing message and add actual response
setMessages((prev) => [
...prev.slice(0, -1),
{ type: "assistant", content: data.answer },
]);
}
} catch (error) {
console.error("Unexpected error in chat widget:", error);
setMessages((prev) => [
...prev.slice(0, -1),
{
type: "assistant",
content:
"An unexpected error occurred. Please refresh the page and try again.",
},
]);
} finally {
setIsProcessing(false);
setIncludeGraphData(false);

View File

@@ -760,6 +760,7 @@ export interface OttoResponse {
answer: string;
documents: OttoDocument[];
success: boolean;
error: boolean;
}
export interface OttoQuery {