Next-level improvements: 15 items across backend, frontend, and testing
Backend: - SQLiteStateBackend: persistent task/trace storage with SQLite - InMemoryStateBackend: in-memory impl of StateBackend interface - Redis cache backend (CacheBackend ABC + MemoryCacheBackend + RedisCacheBackend) - OpenAI adapter: async acomplete() with retry logic - Per-tenant + per-IP rate limiting in middleware Frontend: - State management: useStore + useAppState (zero-dep, context + reducer) - React Router integration: URL-based navigation (usePageNavigation) - WebSocket streaming: sendPrompt + StreamCallbacks for token-by-token updates - File preview: inline image/text/binary preview with expand/collapse - Sparkline charts + MetricCard + BarChart for dashboard visualization - Push notifications hook (useNotifications) with browser Notification API - i18n system: 6 locales (en, es, fr, de, ja, zh) with interpolation - 6 new Storybook stories (ChatMessage, Skeleton, Markdown, SearchFilter, Toast, FilePreview) Testing: - Playwright E2E config + 6 browser specs (desktop + mobile) - 18 new Python tests (SQLiteStateBackend, InMemoryStateBackend, cache backends) 570 Python tests + 45 frontend tests = 615 total, 0 ruff errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
@@ -213,6 +213,57 @@ class OpenAIAdapter(LLMAdapter):
|
||||
raise self._classify_error(last_error) from last_error
|
||||
raise OpenAIAdapterError("All retries exhausted with unknown error")
|
||||
|
||||
async def acomplete(
|
||||
self,
|
||||
messages: list[dict[str, str]],
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
"""Async version of complete using OpenAI's async client.
|
||||
|
||||
Args:
|
||||
messages: List of message dicts with 'role' and 'content'.
|
||||
**kwargs: Additional arguments for the API call.
|
||||
|
||||
Returns:
|
||||
The assistant's response content.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
if not messages:
|
||||
return ""
|
||||
|
||||
try:
|
||||
import openai
|
||||
except ImportError as e:
|
||||
raise ImportError("Install with: pip install fusionagi[openai]") from e
|
||||
|
||||
async_client = openai.AsyncOpenAI(api_key=self._api_key, **self._client_kwargs)
|
||||
model = kwargs.pop("model", self._model)
|
||||
last_error: Exception | None = None
|
||||
delay = self._retry_delay
|
||||
|
||||
for attempt in range(self._max_retries + 1):
|
||||
try:
|
||||
response = await async_client.chat.completions.create(
|
||||
model=model, messages=messages, **kwargs # type: ignore[arg-type]
|
||||
)
|
||||
content = response.choices[0].message.content or ""
|
||||
return content
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
if not self._is_retryable_error(e) or attempt == self._max_retries:
|
||||
break
|
||||
logger.warning(
|
||||
"OpenAI async retry",
|
||||
extra={"attempt": attempt + 1, "error": str(e), "delay": delay},
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
delay = min(delay * self._retry_multiplier, self._max_retry_delay)
|
||||
|
||||
if last_error is not None:
|
||||
raise self._classify_error(last_error) from last_error
|
||||
raise OpenAIAdapterError("All retries exhausted")
|
||||
|
||||
def complete_structured(
|
||||
self,
|
||||
messages: list[dict[str, str]],
|
||||
|
||||
Reference in New Issue
Block a user