diff --git a/app/core/hooks.py b/app/core/hooks.py index 97cd1cd..8acfd59 100644 --- a/app/core/hooks.py +++ b/app/core/hooks.py @@ -50,8 +50,8 @@ class HookRegistry: def __new__(cls) -> HookRegistry: if cls._instance is None: cls._instance = super().__new__(cls) - cls._instance._actions: dict[str, list[tuple[int, Callable]]] = defaultdict(list) - cls._instance._filters: dict[str, list[tuple[int, Callable]]] = defaultdict(list) + cls._instance._actions: dict[str, list[tuple[int, Callable, str]]] = defaultdict(list) + cls._instance._filters: dict[str, list[tuple[int, Callable, str]]] = defaultdict(list) return cls._instance # ─── Registration ─── diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index cf3ce82..013f484 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,3 +1,4 @@ +import { asError } from '@/utils/errorTypes'; import React from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { AppRouter } from '@/routes'; @@ -21,10 +22,10 @@ function QueryClientWrapper({ children }: { children: React.ReactNode }) { }, mutations: { retry: 0, - onError: (error: any) => { + onError: (error: unknown) => { const errObj = asError(error); // Don't show toast for 401 (handled by auth) - if (error?.status === 401) return; - const msg = error?.detail || error?.message || 'Ein Fehler ist aufgetreten'; + if (errObj?.status === 401) return; + const msg = errObj?.detail || errObj?.message || 'Ein Fehler ist aufgetreten'; toast.error(msg); }, }, diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index e247da4..18005d9 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -2,6 +2,7 @@ * Authentication hooks: login, logout, current user, password reset, tenant switching. */ +import { asError } from '@/utils/errorTypes'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiPost, apiGet, setCsrfToken } from './client'; import { useAuthStore } from '@/store/authStore'; @@ -44,8 +45,8 @@ export function useLogin() { setCsrfToken(data.csrf_token); } }, - onError: (error: any) => { - setError(error.message || 'Login failed'); + onError: (error: unknown) => { const errObj = asError(error); + setError(errObj.message || 'Login failed'); }, }); } diff --git a/frontend/src/components/AddressList.tsx b/frontend/src/components/AddressList.tsx index bc778ad..be1828f 100644 --- a/frontend/src/components/AddressList.tsx +++ b/frontend/src/components/AddressList.tsx @@ -1,3 +1,4 @@ +import { asError } from '@/utils/errorTypes'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { apiGet, apiPost, apiPatch, apiDelete } from '@/api/client'; @@ -49,8 +50,8 @@ export function AddressList({ entityType, entityId }: AddressListProps) { `/addresses?entity_type=${entityType}&entity_id=${entityId}` ); setAddresses(data.items); - } catch (err: any) { - setError(err.message || t('common.error')); + } catch (err: unknown) { const errObj = asError(err); + setError(errObj.message || t('common.error')); } finally { setLoading(false); } @@ -65,8 +66,8 @@ export function AddressList({ entityType, entityId }: AddressListProps) { try { await apiDelete(`/addresses/${id}`); await fetchAddresses(); - } catch (err: any) { - setError(err.message || t('common.error')); + } catch (err: unknown) { const errObj = asError(err); + setError(errObj.message || t('common.error')); } }; @@ -74,8 +75,8 @@ export function AddressList({ entityType, entityId }: AddressListProps) { try { await apiPatch(`/addresses/${id}`, { is_default: true }); await fetchAddresses(); - } catch (err: any) { - setError(err.message || t('common.error')); + } catch (err: unknown) { const errObj = asError(err); + setError(errObj.message || t('common.error')); } }; @@ -89,8 +90,8 @@ export function AddressList({ entityType, entityId }: AddressListProps) { setShowForm(false); setEditingAddress(null); await fetchAddresses(); - } catch (err: any) { - setError(err.message || t('common.error')); + } catch (err: unknown) { const errObj = asError(err); + setError(errObj.message || t('common.error')); } }; diff --git a/frontend/src/components/SavedFilters.tsx b/frontend/src/components/SavedFilters.tsx index ae226de..a0f7c9a 100644 --- a/frontend/src/components/SavedFilters.tsx +++ b/frontend/src/components/SavedFilters.tsx @@ -3,6 +3,7 @@ * Integrates into list views (Contacts, Mail, Calendar, DMS). */ +import { asError } from '@/utils/errorTypes'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/Button'; @@ -44,8 +45,8 @@ export function SavedFilters({ entityType, currentCriteria, onLoadFilter }: Save toast.success(t('savedFilters.saved')); setFilterName(''); setSaveModalOpen(false); - } catch (err: any) { - toast.error(err.message || t('common.error')); + } catch (err: unknown) { const errObj = asError(err); + toast.error(errObj.message || t('common.error')); } }; @@ -53,8 +54,8 @@ export function SavedFilters({ entityType, currentCriteria, onLoadFilter }: Save try { await deleteMutation.mutateAsync(id); toast.success(t('savedFilters.deleted')); - } catch (err: any) { - toast.error(err.message || t('common.error')); + } catch (err: unknown) { const errObj = asError(err); + toast.error(errObj.message || t('common.error')); } }; diff --git a/frontend/src/components/agents/AgentChat.tsx b/frontend/src/components/agents/AgentChat.tsx index f6bf270..e87a238 100644 --- a/frontend/src/components/agents/AgentChat.tsx +++ b/frontend/src/components/agents/AgentChat.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { PaperAirplaneIcon, StopCircleIcon, CurrencyDollarIcon } from '@heroicons/react/24/outline'; +import { Send, CircleStop, DollarSign } from 'lucide-react'; interface AgentStep { step_number: number; @@ -40,27 +40,27 @@ export function AgentChat({ agentId, agentName }: AgentChatProps) { const eventSource = new EventSource(url); eventSourceRef.current = eventSource; - eventSource.addEventListener('step', (e) => { + eventSource.addEventListener('step', (e: MessageEvent) => { const step = JSON.parse(e.data) as AgentStep; setSteps((prev) => [...prev, step]); setTotalCost((prev) => prev + (step.cost_usd || 0)); }); - eventSource.addEventListener('status', (e) => { + eventSource.addEventListener('status', (e: MessageEvent) => { const data = JSON.parse(e.data); if (data.status === 'running') { setMessages((prev) => [...prev, `Step ${data.step}: ${data.action || 'Thinking...'}`]); } }); - eventSource.addEventListener('done', (e) => { + eventSource.addEventListener('done', (e: MessageEvent) => { const data = JSON.parse(e.data); setMessages((prev) => [...prev, `Agent: ${data.final_content}`]); setIsRunning(false); eventSource.close(); }); - eventSource.addEventListener('error', (e) => { + eventSource.addEventListener('error', (e: MessageEvent) => { try { const data = JSON.parse(e.data); setMessages((prev) => [...prev, `Error: ${data.error}`]); @@ -93,7 +93,7 @@ export function AgentChat({ agentId, agentName }: AgentChatProps) { {totalCost > 0 && ( - + {totalCost.toFixed(6)} )} @@ -133,11 +133,11 @@ export function AgentChat({ agentId, agentName }: AgentChatProps) { /> {isRunning ? ( ) : ( )} diff --git a/frontend/src/components/agents/AgentEditor.tsx b/frontend/src/components/agents/AgentEditor.tsx index 55950cb..93ff512 100644 --- a/frontend/src/components/agents/AgentEditor.tsx +++ b/frontend/src/components/agents/AgentEditor.tsx @@ -70,7 +70,7 @@ const commonModels = [ export function AgentEditor({ agent, onSaved, onCancel }: AgentEditorProps) { const { t } = useTranslation(); - const { toast } = useToast(); + const toast = useToast(); const { data: tools = [] } = useAgentToolsFull(); const { data: skills = [] } = useAgentSkills(); const createAgent = useCreateAgent(); @@ -83,17 +83,17 @@ export function AgentEditor({ agent, onSaved, onCancel }: AgentEditorProps) { name: agent.name, description: agent.description || '', system_prompt: agent.system_prompt || '', - llm_model: agent.llm_model, + llm_model: agent.llm_model || agent.model || '', tool_ids: agent.tool_ids || [], skill_ids: agent.skill_ids || [], - max_steps: agent.max_steps, - max_duration_seconds: agent.max_duration_seconds, - budget_limit_usd: agent.budget_limit_usd, - temperature: agent.temperature, - max_tokens: agent.max_tokens, - trace_mode: agent.trace_mode, + max_steps: agent.max_steps ?? 20, + max_duration_seconds: agent.max_duration_seconds ?? 300, + budget_limit_usd: agent.budget_limit_usd ?? agent.budget_limit ?? 1.0, + temperature: agent.temperature ?? 0.3, + max_tokens: agent.max_tokens ?? 1000, + trace_mode: (agent.trace_mode === 'extended' ? 'extended' : 'standard') as 'standard' | 'extended', mode: agent.mode, - is_active: agent.is_active, + is_active: agent.is_active ?? agent.active ?? true, trigger_config: agent.trigger_config || {}, ai_use_case_metadata: agent.ai_use_case_metadata || {}, }; @@ -142,28 +142,28 @@ export function AgentEditor({ agent, onSaved, onCancel }: AgentEditorProps) { try { if (agent) { const updated = await updateAgent.mutateAsync({ id: agent.id, data }); - toast({ title: t('agent.saved'), variant: 'success' }); + toast.success(t('agent.saved')); onSaved?.(updated); } else { const created = await createAgent.mutateAsync(data); - toast({ title: t('agent.created'), variant: 'success' }); + toast.success(t('agent.created')); onSaved?.(created); } } catch { - toast({ title: t('agent.saveFailed'), variant: 'error' }); + toast.error(t('agent.saveFailed')); } }; const handleTestRun = async () => { if (!agent) { - toast({ title: t('agent.saveBeforeTest'), variant: 'warning' }); + toast.warning(t('agent.saveBeforeTest')); return; } try { await testRunAgent.mutateAsync(agent.id); - toast({ title: t('agent.testRunOk'), variant: 'success' }); + toast.success(t('agent.testRunOk')); } catch { - toast({ title: t('agent.testRunFailed'), variant: 'error' }); + toast.error(t('agent.testRunFailed')); } }; @@ -268,11 +268,11 @@ export function AgentEditor({ agent, onSaved, onCancel }: AgentEditorProps) { ) : (
{tools.map((tool) => ( -