ravnar 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
_ravnar/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ __all__ = ["__version__"]
2
+
3
+ try:
4
+ from .version import __version__
5
+ except ModuleNotFoundError:
6
+ import warnings
7
+
8
+ warnings.warn("ravnar was not properly installed!", stacklevel=2)
9
+ del warnings
10
+
11
+ __version__ = "UNKNOWN"
@@ -0,0 +1,391 @@
1
+ """This module exist in anticipation of the release of https://github.com/ag-ui-protocol/ag-ui/pull/1307"""
2
+
3
+ __all__ = [
4
+ "AgentCapabilities",
5
+ "ExecutionCapabilities",
6
+ "HumanInTheLoopCapabilities",
7
+ "IdentityCapabilities",
8
+ "MultiAgentCapabilities",
9
+ "MultimodalCapabilities",
10
+ "MultimodalInputCapabilities",
11
+ "MultimodalOutputCapabilities",
12
+ "OutputCapabilities",
13
+ "ReasoningCapabilities",
14
+ "StateCapabilities",
15
+ "SubAgentInfo",
16
+ "ToolsCapabilities",
17
+ "TransportCapabilities",
18
+ ]
19
+
20
+ from typing import Any
21
+
22
+ from ag_ui.core.types import ConfiguredBaseModel, Tool
23
+ from pydantic import Field
24
+
25
+
26
+ class SubAgentInfo(ConfiguredBaseModel):
27
+ """Describes a sub-agent that can be invoked by a parent agent."""
28
+
29
+ name: str = Field(description="Unique name or identifier of the sub-agent.")
30
+ description: str | None = Field(
31
+ default=None,
32
+ description="What this sub-agent specializes in. Helps clients build agent selection UIs.",
33
+ )
34
+
35
+
36
+ class IdentityCapabilities(ConfiguredBaseModel):
37
+ """Basic metadata about the agent.
38
+
39
+ Useful for discovery UIs, agent marketplaces, and debugging. Set these when you want clients to display agent
40
+ information or when multiple agents are available and users need to pick one.
41
+ """
42
+
43
+ name: str | None = Field(
44
+ default=None,
45
+ description="Human-readable name shown in UIs and agent selectors.",
46
+ )
47
+ type: str | None = Field(
48
+ default=None,
49
+ description='The framework or platform powering this agent (e.g., "langgraph", "mastra", "crewai").',
50
+ )
51
+ description: str | None = Field(
52
+ default=None,
53
+ description="What this agent does — helps users and routing logic decide when to use it.",
54
+ )
55
+ version: str | None = Field(
56
+ default=None,
57
+ description='Semantic version of the agent (e.g., "1.2.0"). Useful for compatibility checks.',
58
+ )
59
+ provider: str | None = Field(default=None, description="Organization or team that maintains this agent.")
60
+ documentation_url: str | None = Field(default=None, description="URL to the agent's documentation or homepage.")
61
+ metadata: dict[str, Any] = Field(
62
+ default_factory=dict,
63
+ description="Arbitrary key-value pairs for integration-specific identity info.",
64
+ )
65
+
66
+
67
+ class TransportCapabilities(ConfiguredBaseModel):
68
+ """Declares which transport mechanisms the agent supports.
69
+
70
+ Clients use this to pick the best connection strategy. Only set flags to `True` for transports your agent actually
71
+ handles — omit or set `False` for unsupported ones.
72
+ """
73
+
74
+ streaming: bool | None = Field(
75
+ default=None,
76
+ description="Set `True` if the agent streams responses via SSE. Most agents enable this.",
77
+ )
78
+ websocket: bool | None = Field(
79
+ default=None,
80
+ description="Set `True` if the agent accepts persistent WebSocket connections.",
81
+ )
82
+ http_binary: bool | None = Field(
83
+ default=None,
84
+ description="Set `True` if the agent supports the AG-UI binary protocol (protobuf over HTTP).",
85
+ )
86
+ push_notifications: bool | None = Field(
87
+ default=None,
88
+ description="Set `True` if the agent can send async updates via webhooks after a run finishes.",
89
+ )
90
+ resumable: bool | None = Field(
91
+ default=None,
92
+ description="Set `True` if the agent supports resuming interrupted streams via sequence numbers.",
93
+ )
94
+
95
+
96
+ class ToolsCapabilities(ConfiguredBaseModel):
97
+ """Tool calling capabilities.
98
+
99
+ Distinguishes between tools the agent itself provides (listed in `items`) and tools the client passes at runtime
100
+ via `RunAgentInput.tools`. Enable this when your agent can call functions, search the web, execute code, etc.
101
+ """
102
+
103
+ supported: bool | None = Field(
104
+ default=None,
105
+ description=(
106
+ "Set `True` if the agent can make tool calls at all. Set `False` to explicitly signal tool calling is "
107
+ "disabled even if items are present."
108
+ ),
109
+ )
110
+ items: list[Tool] = Field(
111
+ default_factory=list,
112
+ description=(
113
+ "The tools this agent provides on its own (full JSON Schema definitions). These are distinct from "
114
+ "client-provided tools passed in `RunAgentInput.tools`."
115
+ ),
116
+ )
117
+ parallel_calls: bool | None = Field(
118
+ default=None,
119
+ description="Set `True` if the agent can invoke multiple tools concurrently within a single step.",
120
+ )
121
+ client_provided: bool | None = Field(
122
+ default=None,
123
+ description="Set `True` if the agent accepts and uses tools provided by the client at runtime.",
124
+ )
125
+
126
+
127
+ class OutputCapabilities(ConfiguredBaseModel):
128
+ """Output format support.
129
+
130
+ Enable `structured_output` when your agent can return responses conforming to a JSON schema, which is useful for
131
+ programmatic consumption.
132
+ """
133
+
134
+ structured_output: bool | None = Field(
135
+ default=None,
136
+ description="Set `True` if the agent can produce structured JSON output matching a provided schema.",
137
+ )
138
+ supported_mime_types: list[str] = Field(
139
+ default_factory=list,
140
+ description=(
141
+ 'MIME types the agent can produce (e.g., `["text/plain", "application/json"]`). Omit if the agent only '
142
+ "produces plain text."
143
+ ),
144
+ )
145
+
146
+
147
+ class StateCapabilities(ConfiguredBaseModel):
148
+ """State and memory management capabilities.
149
+
150
+ These tell the client how the agent handles shared state and whether conversation context persists across runs.
151
+ """
152
+
153
+ snapshots: bool | None = Field(
154
+ default=None,
155
+ description="Set `True` if the agent emits `STATE_SNAPSHOT` events (full state replacement).",
156
+ )
157
+ deltas: bool | None = Field(
158
+ default=None,
159
+ description="Set `True` if the agent emits `STATE_DELTA` events (JSON Patch incremental updates).",
160
+ )
161
+ memory: bool | None = Field(
162
+ default=None,
163
+ description=(
164
+ "Set `True` if the agent has long-term memory beyond the current thread (e.g., vector store, knowledge "
165
+ "base, or cross-session recall)."
166
+ ),
167
+ )
168
+ persistent_state: bool | None = Field(
169
+ default=None,
170
+ description=(
171
+ "Set `True` if state is preserved across multiple runs within the same thread. When `False`, state resets "
172
+ "on each run."
173
+ ),
174
+ )
175
+
176
+
177
+ class MultiAgentCapabilities(ConfiguredBaseModel):
178
+ """Multi-agent coordination capabilities.
179
+
180
+ Enable these when your agent can orchestrate or hand off work to other agents.
181
+ """
182
+
183
+ supported: bool | None = Field(
184
+ default=None,
185
+ description="Set `True` if the agent participates in any form of multi-agent coordination.",
186
+ )
187
+ delegation: bool | None = Field(
188
+ default=None,
189
+ description="Set `True` if the agent can delegate subtasks to other agents while retaining control.",
190
+ )
191
+ handoffs: bool | None = Field(
192
+ default=None,
193
+ description="Set `True` if the agent can transfer the conversation entirely to another agent.",
194
+ )
195
+ sub_agents: list[SubAgentInfo] = Field(
196
+ default_factory=list,
197
+ description="List of sub-agents this agent can invoke. Helps clients build agent selection UIs.",
198
+ )
199
+
200
+
201
+ class ReasoningCapabilities(ConfiguredBaseModel):
202
+ """Reasoning and thinking capabilities.
203
+
204
+ Enable these when your agent exposes its internal thought process (e.g., chain-of-thought, extended thinking).
205
+ """
206
+
207
+ supported: bool | None = Field(
208
+ default=None,
209
+ description="Set `True` if the agent produces reasoning/thinking tokens visible to the client.",
210
+ )
211
+ streaming: bool | None = Field(
212
+ default=None,
213
+ description="Set `True` if reasoning tokens are streamed incrementally (vs. returned all at once).",
214
+ )
215
+ encrypted: bool | None = Field(
216
+ default=None,
217
+ description=(
218
+ "Set `True` if reasoning content is encrypted (zero-data-retention mode). Clients should expect opaque "
219
+ "`encrypted_value` fields instead of readable content."
220
+ ),
221
+ )
222
+
223
+
224
+ class MultimodalInputCapabilities(ConfiguredBaseModel):
225
+ """Modalities the agent can accept as input.
226
+
227
+ Clients use this to show/hide file upload buttons, audio recorders, image pickers, etc.
228
+ """
229
+
230
+ image: bool | None = Field(
231
+ default=None,
232
+ description="Set `True` if the agent can process image inputs (e.g., screenshots, photos).",
233
+ )
234
+ audio: bool | None = Field(
235
+ default=None,
236
+ description="Set `True` if the agent can process audio inputs (speech, recordings).",
237
+ )
238
+ video: bool | None = Field(default=None, description="Set `True` if the agent can process video inputs.")
239
+ pdf: bool | None = Field(default=None, description="Set `True` if the agent can process PDF documents.")
240
+ file: bool | None = Field(
241
+ default=None,
242
+ description="Set `True` if the agent can process arbitrary file uploads.",
243
+ )
244
+
245
+
246
+ class MultimodalOutputCapabilities(ConfiguredBaseModel):
247
+ """Modalities the agent can produce as output.
248
+
249
+ Clients use this to anticipate rich content in the agent's response.
250
+ """
251
+
252
+ image: bool | None = Field(
253
+ default=None,
254
+ description="Set `True` if the agent can generate images as part of its response.",
255
+ )
256
+ audio: bool | None = Field(
257
+ default=None,
258
+ description="Set `True` if the agent can produce audio output (text-to-speech, audio files).",
259
+ )
260
+
261
+
262
+ class MultimodalCapabilities(ConfiguredBaseModel):
263
+ """Multimodal input and output support.
264
+
265
+ Organized into `input` and `output` sub-objects so clients can independently query what the agent accepts versus
266
+ what it produces.
267
+ """
268
+
269
+ input: MultimodalInputCapabilities = Field(
270
+ default_factory=MultimodalInputCapabilities,
271
+ description="Modalities the agent can accept as input (images, audio, video, PDFs, files).",
272
+ )
273
+ output: MultimodalOutputCapabilities = Field(
274
+ default_factory=MultimodalOutputCapabilities,
275
+ description="Modalities the agent can produce as output (images, audio).",
276
+ )
277
+
278
+
279
+ class ExecutionCapabilities(ConfiguredBaseModel):
280
+ """Execution control and limits.
281
+
282
+ Declare these so clients can set expectations about how long or how many steps an agent run might take.
283
+ """
284
+
285
+ code_execution: bool | None = Field(
286
+ default=None,
287
+ description="Set `True` if the agent can execute code (e.g., Python, JavaScript) during a run.",
288
+ )
289
+ sandboxed: bool | None = Field(
290
+ default=None,
291
+ description=(
292
+ "Set `True` if code execution happens in a sandboxed/isolated environment. Only meaningful when "
293
+ "`code_execution` is `True`."
294
+ ),
295
+ )
296
+ max_iterations: int | None = Field(
297
+ default=None,
298
+ description=(
299
+ "Maximum number of tool-call/reasoning iterations the agent will perform per run. Helps clients display"
300
+ "progress or set timeout expectations."
301
+ ),
302
+ )
303
+ max_execution_time: int | None = Field(
304
+ default=None,
305
+ description="Maximum wall-clock time (in milliseconds) the agent will run before timing out.",
306
+ )
307
+
308
+
309
+ class HumanInTheLoopCapabilities(ConfiguredBaseModel):
310
+ """Human-in-the-loop interaction support.
311
+
312
+ Enable these when your agent can pause execution to request human input, approval, or feedback before continuing.
313
+ """
314
+
315
+ supported: bool | None = Field(
316
+ default=None,
317
+ description="Set `True` if the agent supports any form of human-in-the-loop interaction.",
318
+ )
319
+ approvals: bool | None = Field(
320
+ default=None,
321
+ description=(
322
+ "Set `True` if the agent can pause and request explicit approval before performing sensitive actions "
323
+ "(e.g., sending emails, deleting data)."
324
+ ),
325
+ )
326
+ interventions: bool | None = Field(
327
+ default=None,
328
+ description="Set `True` if the agent allows humans to intervene and modify its plan mid-execution.",
329
+ )
330
+ feedback: bool | None = Field(
331
+ default=None,
332
+ description=(
333
+ "Set `True` if the agent can incorporate user feedback (thumbs up/down, corrections) to improve its "
334
+ "behavior within the current session."
335
+ ),
336
+ )
337
+
338
+
339
+ class AgentCapabilities(ConfiguredBaseModel):
340
+ """A categorized snapshot of an agent's current capabilities.
341
+
342
+ All fields are optional — agents only declare what they support. Omitted fields mean the capability is not declared
343
+ (unknown), not that it's unsupported.
344
+
345
+ The `custom` field is an escape hatch for integration-specific capabilities that don't fit into the standard
346
+ categories.
347
+ """
348
+
349
+ identity: IdentityCapabilities = Field(
350
+ default_factory=IdentityCapabilities, description="Agent identity and metadata."
351
+ )
352
+ transport: TransportCapabilities = Field(
353
+ default_factory=TransportCapabilities,
354
+ description="Supported transport mechanisms (SSE, WebSocket, binary, etc.).",
355
+ )
356
+ tools: ToolsCapabilities = Field(
357
+ default_factory=ToolsCapabilities,
358
+ description="Tools the agent provides and tool calling configuration.",
359
+ )
360
+ output: OutputCapabilities = Field(
361
+ default_factory=OutputCapabilities,
362
+ description="Output format support (structured output, MIME types).",
363
+ )
364
+ state: StateCapabilities = Field(
365
+ default_factory=StateCapabilities,
366
+ description="State and memory management (snapshots, deltas, persistence).",
367
+ )
368
+ multi_agent: MultiAgentCapabilities = Field(
369
+ default_factory=MultiAgentCapabilities,
370
+ description="Multi-agent coordination (delegation, handoffs, sub-agents).",
371
+ )
372
+ reasoning: ReasoningCapabilities = Field(
373
+ default_factory=ReasoningCapabilities,
374
+ description="Reasoning and thinking support (chain-of-thought, encrypted thinking).",
375
+ )
376
+ multi_modal: MultimodalCapabilities = Field(
377
+ default_factory=MultimodalCapabilities,
378
+ description="Multimodal input/output support (images, audio, video, files).",
379
+ )
380
+ execution: ExecutionCapabilities = Field(
381
+ default_factory=ExecutionCapabilities,
382
+ description="Execution control and limits (code execution, timeouts, iteration caps).",
383
+ )
384
+ human_in_the_loop: HumanInTheLoopCapabilities = Field(
385
+ default_factory=HumanInTheLoopCapabilities,
386
+ description="Human-in-the-loop support (approvals, interventions, feedback).",
387
+ )
388
+ custom: dict[str, Any] = Field(
389
+ default_factory=dict,
390
+ description="Integration-specific capabilities not covered by the standard categories.",
391
+ )
_ravnar/agents.py ADDED
@@ -0,0 +1,165 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ import textwrap
5
+ import uuid
6
+ from collections.abc import AsyncIterator
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ import ag_ui.core
10
+ import pydantic
11
+
12
+ from _ravnar.ag_ui_compatibilities import AgentCapabilities, IdentityCapabilities, TransportCapabilities
13
+
14
+ from .mixin import SetupTeardownMixin
15
+
16
+ if TYPE_CHECKING:
17
+ import agno.agent
18
+ import pydantic_ai
19
+
20
+ from _ravnar.schema import QuickPrompt
21
+
22
+
23
+ class Agent(abc.ABC, SetupTeardownMixin):
24
+ """Agent base class"""
25
+
26
+ @abc.abstractmethod
27
+ def run(self, input: ag_ui.core.RunAgentInput) -> AsyncIterator[ag_ui.core.Event]: ...
28
+
29
+ def get_capabilities(self) -> AgentCapabilities:
30
+ """The capabilities of the agent."""
31
+ return AgentCapabilities(transport=TransportCapabilities(streaming=True))
32
+
33
+ def get_quick_prompts(self) -> list[QuickPrompt]:
34
+ """The quick prompts of the agent."""
35
+ return []
36
+
37
+
38
+ class DefaultAgent(Agent):
39
+ async def run(self, input: ag_ui.core.RunAgentInput) -> AsyncIterator[ag_ui.core.Event]:
40
+ message_id = str(uuid.uuid4())
41
+ message = """
42
+ Hello, I'm ravnar's default agent.
43
+ Unfortunately, I'm not terribly helpful right now.
44
+ """
45
+
46
+ yield ag_ui.core.RunStartedEvent(
47
+ thread_id=input.thread_id, run_id=input.run_id, parent_run_id=input.parent_run_id, input=input
48
+ )
49
+ yield ag_ui.core.TextMessageStartEvent(message_id=message_id)
50
+ for delta in textwrap.dedent(message.strip()).split():
51
+ yield ag_ui.core.TextMessageContentEvent(message_id=message_id, delta=delta)
52
+ yield ag_ui.core.TextMessageEndEvent(message_id=message_id)
53
+ yield ag_ui.core.RunFinishedEvent(thread_id=input.thread_id, run_id=input.run_id)
54
+
55
+
56
+ class _AgentBase(Agent):
57
+ def __init__(
58
+ self,
59
+ *,
60
+ capabilities: AgentCapabilities | None = None,
61
+ quick_prompts: list[QuickPrompt] | None = None,
62
+ ):
63
+ if capabilities is None:
64
+ capabilities = super().get_capabilities()
65
+ self._capabilities = capabilities
66
+
67
+ if quick_prompts is None:
68
+ quick_prompts = super().get_quick_prompts()
69
+ self._quick_prompts = quick_prompts
70
+
71
+ def get_capabilities(self) -> AgentCapabilities:
72
+ """The capabilities of the agent."""
73
+ return self._capabilities
74
+
75
+ def get_quick_prompts(self) -> list[QuickPrompt]:
76
+ """The quick prompts of the agent."""
77
+ return self._quick_prompts
78
+
79
+
80
+ class SSEAgent(_AgentBase):
81
+ """SSE Agent"""
82
+
83
+ def __init__(
84
+ self,
85
+ method: str,
86
+ url: str,
87
+ *,
88
+ client_kwargs: dict[str, Any] | None = None,
89
+ capabilities: AgentCapabilities | None = None,
90
+ quick_prompts: list[QuickPrompt] | None = None,
91
+ ):
92
+ self._method = method
93
+ self._url = url
94
+ if client_kwargs is None:
95
+ client_kwargs = {}
96
+ self._client_kwargs = client_kwargs
97
+
98
+ super().__init__(capabilities=capabilities, quick_prompts=quick_prompts)
99
+
100
+ async def run(self, input: ag_ui.core.RunAgentInput) -> AsyncIterator[ag_ui.core.Event]:
101
+ import httpx
102
+ import httpx_sse
103
+
104
+ async with (
105
+ httpx.AsyncClient() as client,
106
+ httpx_sse.aconnect_sse(
107
+ client,
108
+ self._method,
109
+ self._url,
110
+ json=input.model_dump(mode="json"),
111
+ ) as event_source,
112
+ ):
113
+ event_source.response.raise_for_status()
114
+
115
+ ta: pydantic.TypeAdapter[ag_ui.core.Event] = pydantic.TypeAdapter(ag_ui.core.Event)
116
+ async for sse in event_source.aiter_sse():
117
+ yield ta.validate_json(sse.data)
118
+
119
+
120
+ class PydanticAiAgentWrapper(_AgentBase):
121
+ """Pydantic AI agent wrapper"""
122
+
123
+ def __init__(
124
+ self,
125
+ agent: pydantic_ai.Agent,
126
+ *,
127
+ identity: IdentityCapabilities | None = None,
128
+ quick_prompts: list[QuickPrompt] | None = None,
129
+ ) -> None:
130
+ self._agent = agent
131
+
132
+ if identity is None:
133
+ identity = IdentityCapabilities(name=agent.name)
134
+ capabilities = AgentCapabilities(identity=identity, transport=TransportCapabilities(streaming=True))
135
+
136
+ super().__init__(capabilities=capabilities, quick_prompts=quick_prompts)
137
+
138
+ def run(self, input: ag_ui.core.RunAgentInput) -> AsyncIterator[ag_ui.core.Event]:
139
+ from pydantic_ai.ui.ag_ui import AGUIAdapter
140
+
141
+ return AGUIAdapter(agent=self._agent, run_input=input, accept="text/event-stream").run_stream() # type: ignore[return-value]
142
+
143
+
144
+ class AgnoAgentWrapper(_AgentBase):
145
+ """Agno agent wrapper"""
146
+
147
+ def __init__(
148
+ self,
149
+ agent: agno.agent.Agent,
150
+ *,
151
+ identity: IdentityCapabilities | None = None,
152
+ quick_prompts: list[QuickPrompt] | None = None,
153
+ ) -> None:
154
+ self._agent = agent
155
+
156
+ if identity is None:
157
+ identity = IdentityCapabilities(name=agent.name)
158
+ capabilities = AgentCapabilities(identity=identity, transport=TransportCapabilities(streaming=True))
159
+
160
+ super().__init__(capabilities=capabilities, quick_prompts=quick_prompts)
161
+
162
+ def run(self, input: ag_ui.core.RunAgentInput) -> AsyncIterator[ag_ui.core.Event]:
163
+ from agno.os.interfaces.agui.router import run_agent
164
+
165
+ return run_agent(self._agent, input) # type: ignore[return-value]
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ from fastapi import Depends
7
+
8
+ from _ravnar import schema
9
+
10
+ from .agents import make_router as make_agents_router
11
+ from .threads import make_router as make_threads_router
12
+
13
+ if TYPE_CHECKING:
14
+ from _ravnar.core import AgentHandler
15
+ from _ravnar.database import Database
16
+
17
+
18
+ def make_router(
19
+ *, database: Database, agent_handler: AgentHandler, authenticated_user: Callable[..., Any]
20
+ ) -> schema.APIRouter:
21
+ router = schema.APIRouter(tags=["API"], dependencies=[Depends(authenticated_user)])
22
+
23
+ @router.get("/user")
24
+ async def get_user(
25
+ user: schema.User = Depends(authenticated_user), # noqa: B008
26
+ ) -> schema.User:
27
+ return user
28
+
29
+ # FIXME: cache
30
+ @router.get("/config")
31
+ async def get_config() -> schema.APIConfig:
32
+ return schema.APIConfig(agents=agent_handler.configs)
33
+
34
+ router.include_router(
35
+ make_threads_router(database=database, agent_handler=agent_handler, authenticated_user=authenticated_user),
36
+ prefix="/threads",
37
+ )
38
+
39
+ router.include_router(
40
+ make_agents_router(agent_handler=agent_handler, authenticated_user=authenticated_user), prefix="/agents"
41
+ )
42
+
43
+ return router
_ravnar/api/agents.py ADDED
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import TYPE_CHECKING, Annotated, Any
5
+
6
+ import ag_ui.core
7
+ import ag_ui.encoder
8
+ import fastsse
9
+ from fastapi import Depends, Path
10
+
11
+ from _ravnar import schema
12
+
13
+ if TYPE_CHECKING:
14
+ from . import AgentHandler
15
+
16
+
17
+ def make_router(*, agent_handler: AgentHandler, authenticated_user: Callable[..., Any]) -> schema.APIRouter:
18
+ router = schema.APIRouter(tags=["Agents"], dependencies=[Depends(authenticated_user)])
19
+
20
+ @router.sse("/{agentId}/run", methods=["POST"], response_model=schema.Event, tags=["Runs"])
21
+ async def create_stateless_run(
22
+ *, agent_id: Annotated[str, Path(alias="agentId")], run_agent_input: ag_ui.core.RunAgentInput
23
+ ) -> fastsse.Response:
24
+ return await agent_handler.run(agent_id, run_agent_input)
25
+
26
+ return router