Skip to content

Commit 599f12b

Browse files
author
Erez Sharim
committed
fix: undefined agent runner
- when trying to create an agent, check if the agent type exists - server now checks requested agent runner type exists before trying to run against it. - ui brings up a toast to indicate "agent doesn't exist" error
1 parent 05e2479 commit 599f12b

File tree

7 files changed

+81
-21
lines changed

7 files changed

+81
-21
lines changed

chat/src/components/chat/multimodal-input.tsx

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function PureMultimodalInput({
3030
handleSubmit: (
3131
event?: {
3232
preventDefault?: () => void;
33-
},
33+
}
3434
// chatRequestOptions?: ChatRequestOptions,
3535
) => void;
3636
hasMessages: boolean;
@@ -67,7 +67,7 @@ function PureMultimodalInput({
6767

6868
const [localStorageInput, setLocalStorageInput] = useLocalStorage(
6969
"input",
70-
"",
70+
""
7171
);
7272

7373
// biome-ignore lint/correctness/useExhaustiveDependencies: Only run once after hydration
@@ -92,19 +92,37 @@ function PureMultimodalInput({
9292

9393
const submitForm = useCallback(async () => {
9494
if (newSession) {
95-
await fetchClientWithThrow.POST("/api/sessions", {
96-
body: {
97-
id: sessionId,
98-
title: input.split(" ").reduce((acc, word) => {
99-
if (acc.length + word.length + 1 <= 15) {
100-
return acc + (acc ? " " : "") + word;
101-
}
102-
return acc;
103-
}, ""),
104-
agent_id: agentId,
105-
},
106-
});
107-
queyrClient.invalidateQueries({ queryKey: ["get", "/api/sessions", {}] });
95+
try {
96+
await fetchClientWithThrow.POST("/api/sessions", {
97+
body: {
98+
id: sessionId,
99+
title: input.split(" ").reduce((acc, word) => {
100+
if (acc.length + word.length + 1 <= 15) {
101+
return acc + (acc ? " " : "") + word;
102+
}
103+
return acc;
104+
}, ""),
105+
agent_id: agentId,
106+
},
107+
});
108+
queyrClient.invalidateQueries({
109+
queryKey: ["get", "/api/sessions", {}],
110+
});
111+
} catch (error: any) {
112+
const str = error.message || error.toString();
113+
const jsonMatch = str.match(/{.*}/);
114+
let err = "Failed to create new session";
115+
if (jsonMatch) {
116+
const parsed = JSON.parse(jsonMatch[0]);
117+
const detail = parsed.detail;
118+
if (detail) {
119+
err = `Failed to create new session: ${detail}`;
120+
}
121+
}
122+
123+
toast.error(err);
124+
return;
125+
}
108126
}
109127

110128
// navigate({ to: '/session/$sessionId', params: { sessionId: sessionIdToRedir } });
@@ -180,7 +198,7 @@ export const MultimodalInput = memo(
180198
if (prevProps.sessionId !== nextProps.sessionId) return false;
181199

182200
return true;
183-
},
201+
}
184202
);
185203

186204
function PureStopButton({
@@ -209,7 +227,10 @@ const StopButton = memo(PureStopButton);
209227
function PureSendButton({
210228
submitForm,
211229
input,
212-
}: { submitForm: () => void; input: string }) {
230+
}: {
231+
submitForm: () => void;
232+
input: string;
233+
}) {
213234
return (
214235
<Button
215236
className="rounded-full p-1.5 h-fit border dark:border-zinc-600"

packages/api/src/flux0_api/agents.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
from flux0_api.auth import AuthedUser
77
from flux0_api.common import apigen_config, example_json_content
8-
from flux0_api.dependency_injection import get_agent_store
8+
from flux0_api.dependency_injection import get_agent_store, get_session_service
9+
from flux0_api.session_service import SessionService
910
from flux0_api.types_agents import (
1011
AgentCreationParamsDTO,
1112
AgentDTO,
@@ -19,7 +20,9 @@
1920

2021
def mount_create_agent_route(
2122
router: APIRouter,
22-
) -> Callable[[AuthedUser, AgentCreationParamsDTO, AgentStore], Coroutine[None, Any, AgentDTO]]:
23+
) -> Callable[
24+
[AuthedUser, AgentCreationParamsDTO, AgentStore, SessionService], Coroutine[None, Any, AgentDTO]
25+
]:
2326
@router.post(
2427
"",
2528
tags=[API_GROUP],
@@ -42,8 +45,17 @@ async def create_agent(
4245
authedUser: AuthedUser,
4346
params: AgentCreationParamsDTO,
4447
agent_store: AgentStore = Depends(get_agent_store),
48+
session_service: SessionService = Depends(get_session_service),
4549
) -> AgentDTO:
4650
"""Creates a new agent with the specified parameters."""
51+
52+
# Ensure the agent type is supported by the agent runner factory.
53+
if not session_service._agent_runner_factory.runner_exists(params.type):
54+
raise HTTPException(
55+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
56+
detail=f"Agent type '{params.type}' is not supported",
57+
)
58+
4759
agent = await agent_store.create_agent(
4860
name=params and params.name or "Unnamed Agent",
4961
type=params.type,

packages/api/src/flux0_api/sessions.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ def mount_create_session_route(
7676
"description": "Session created successfully, Returns the created session along with its generated ID.",
7777
"content": example_json_content(session_example),
7878
},
79+
status.HTTP_400_BAD_REQUEST: {
80+
"description": "Invalid request parameters, such as missing agent ID or invalid session ID format."
81+
},
7982
status.HTTP_422_UNPROCESSABLE_ENTITY: {
8083
"description": "Validation error in request parameters"
8184
},
@@ -90,7 +93,7 @@ async def create_session_route(
9093
allow_greeting: AllowGreetingQuery = False,
9194
) -> SessionDTO:
9295
"""
93-
Create a new session bettween a user and an agent.
96+
Create a new session between a user and an agent.
9497
9598
The session will be associated with the provided agent and optionally with a user.
9699
If no user is provided, a guest user will be created.
@@ -102,6 +105,12 @@ async def create_session_route(
102105
detail=f"Agent with ID {params.agent_id} not found",
103106
)
104107

108+
if not session_service._agent_runner_factory.runner_exists(agent.type):
109+
raise HTTPException(
110+
status_code=status.HTTP_400_BAD_REQUEST,
111+
detail=f"Agent type {agent.type} is not supported by the server",
112+
)
113+
105114
session = await session_service.create_user_session(
106115
id=params.id,
107116
user_id=authedUser.id,

packages/api/tests/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ def __init__(self, runner_class: type[AgentRunner] = DummyAgentRunner) -> None:
122122
def create_runner(self, agent_type: AgentType) -> AgentRunner:
123123
return self.runner_class() # Instantiate the injected runner
124124

125+
def runner_exists(self, agent_type: AgentType) -> bool:
126+
# For simplicity, we assume the injected runner always exists
127+
return True
128+
125129

126130
@pytest.fixture
127131
def agent_runner_factory() -> AgentRunnerFactory:

packages/api/tests/test_agents.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
mount_list_agents_route,
99
mount_retrieve_agent_route,
1010
)
11+
from flux0_api.session_service import SessionService
1112
from flux0_api.types_agents import AgentCreationParamsDTO, AgentDTO
1213
from flux0_core.agents import Agent, AgentId, AgentStore
1314
from flux0_core.ids import gen_id
@@ -18,6 +19,7 @@ async def test_create_agent_success(
1819
user: User,
1920
agent: Agent,
2021
agent_store: AgentStore,
22+
session_service: SessionService,
2123
) -> None:
2224
router = APIRouter()
2325

@@ -26,7 +28,7 @@ async def test_create_agent_success(
2628

2729
# Create a dummy agent creation DTO. Adjust fields as needed.
2830
params = AgentCreationParamsDTO(name=agent.name, type=agent.type, description=agent.description)
29-
result: AgentDTO = await create_route(user, params, agent_store)
31+
result: AgentDTO = await create_route(user, params, agent_store, session_service)
3032

3133
# Assert the returned agent has expected values.
3234
assert result.model_dump(exclude={"id", "created_at"}) == {

packages/core/src/flux0_core/agent_runners/api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class AgentRunnerFactory(ABC):
4343
@abstractmethod
4444
def create_runner(self, agent_type: AgentType) -> AgentRunner: ...
4545

46+
@abstractmethod
47+
def runner_exists(self, agent_type: AgentType) -> bool: ...
48+
4649

4750
T = TypeVar("T", bound=AgentRunner)
4851

packages/server/src/flux0_server/container_factory.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,12 @@ def create_runner(self, agent_type: AgentType) -> AgentRunner:
1717
if issubclass(defi, AgentRunner) and getattr(defi, "agent_type") == agent_type:
1818
return self.container[defi]
1919
raise ValueError(f"No engine found for agent type {agent_type}")
20+
21+
@override
22+
def runner_exists(self, agent_type: AgentType) -> bool:
23+
for defi in self.container.defined_types:
24+
if not isinstance(defi, type):
25+
continue
26+
if issubclass(defi, AgentRunner) and getattr(defi, "agent_type") == agent_type:
27+
return True
28+
return False

0 commit comments

Comments
 (0)