Conversation
📝 WalkthroughWalkthroughAdds HTTP server support and session lifecycle for Vision Agents: introduces AgentSession and session APIs in AgentLauncher, a FastAPI-based HTTP router and models, Runner to serve or run agents (CLI/HTTP), observability hooks, logging tweaks, tests, and example server project files. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as HTTP Client
participant API as FastAPI Router
participant Launcher as AgentLauncher
participant Agent as Agent
participant Sessions as Sessions Store
Client->>API: POST /sessions {call_id}
API->>Launcher: start_session(call_id)
Launcher->>Launcher: _create_agent()
Launcher->>Agent: initialize
Launcher->>Sessions: track AgentSession
Launcher->>Launcher: spawn join task
API->>Client: 201 {session_id}
Note over Launcher: Session runs asynchronously
Launcher->>Agent: _join_call(call_type, call_id)
Agent->>Agent: process call
Client->>API: GET /sessions/{session_id}/metrics
API->>Launcher: get_session(session_id)
API->>Agent: agent.metrics
API->>Client: 200 {metrics}
Client->>API: DELETE /sessions/{session_id}
API->>Launcher: close_session(session_id)
Launcher->>Sessions: cancel/await join task
Sessions->>Sessions: cleanup
API->>Client: 204
sequenceDiagram
participant User as CLI User
participant Runner as Runner
participant Launcher as AgentLauncher
participant SDK as SDK Logger
participant Agent as Agent
participant Demo as Demo UI
User->>Runner: run(call_id, call_type)
Runner->>SDK: configure_sdk_logger()
Runner->>Launcher: start()
Launcher->>Agent: warmup
Runner->>Agent: launch()
alt Agent has open_demo_for_agent
Runner->>Demo: open UI
end
Runner->>Launcher: start_session(call_id)
Launcher->>Launcher: spawn join task
Runner->>Launcher: await session.wait()
Note over Launcher: Processing call
Launcher->>Launcher: task completes
Runner->>Launcher: stop()
Runner->>User: return
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In @agents-core/vision_agents/core/agents/agent_launcher.py:
- Around line 29-30: The type hints for create_agent and join_call use invalid
Coroutine generics; replace those malformatted types with correct generics such
as Coroutine[Any, Any, Agent] for create_agent’s return and Coroutine[Any, Any,
None] for join_call, or alternatively use Awaitable[Agent] and Awaitable[None]
from typing; update the annotations on create_agent, join_call and any imports
(e.g., add Any or Awaitable) accordingly so the signatures correctly express the
asynchronous return types for Agent and None.
In @agents-core/vision_agents/core/runners/http/routes.py:
- Around line 76-81: The current block catching a bare Exception around
launcher.join should be changed to catch the specific exceptions launcher.join
can raise (e.g., RuntimeError, asyncio.CancelledError, OSError) by replacing
"except Exception as e" with explicit except clauses for those exceptions and
handling each with logger.exception and raising the HTTPException; if you cannot
yet enumerate them, leave a single broad catch only after the specific catches
and add a clear comment above the block explaining why a catch-all is
temporarily required and that it must be narrowed before production, and ensure
the logger.exception and HTTPException detail remain intact (refer to
launcher.join, logger.exception, and HTTPException in this change).
- Around line 143-151: In the ready function, replace the incorrect
Response(200) call with a proper Response using the status_code kwarg (e.g.,
Response(status_code=200)) and remove the unnecessary else by returning early:
check launcher.warmed_up and return the 200 Response when true, otherwise return
the JSONResponse with status_code=400; reference the ready function and
launcher.warmed_up to locate the change.
- Around line 135-140: The alive route is creating a Response incorrectly by
passing 200 as the content; update the handler (the async function alive
decorated with @router.get("/alive")) to instantiate Response with the
status_code keyword (e.g., Response(status_code=200) or Response(content="",
status_code=200)) so the HTTP status is set explicitly rather than placed into
the response body.
- Line 33: The router-level lifespan passed into APIRouter (router =
APIRouter(lifespan=lifespan)) will not run when the router is mounted into a
FastAPI app; move the lifespan registration to the FastAPI app instead by
removing lifespan from APIRouter and passing the lifespan context manager to the
FastAPI constructor where the app is created (ensure any setup/teardown logic
currently in the router-level lifespan is invoked by the app-level lifespan);
update references to router and lifespan and remove the router-level lifespan
parameter so lifecycle handlers run correctly when the router is included.
In @agents-core/vision_agents/core/runners/http/runner.py:
- Around line 34-40: Replace the getattr-based lookup for numeric_level in the
run method with an explicit mapping: create a dict mapping common level names
("CRITICAL","ERROR","WARNING","INFO","DEBUG","NOTSET") to their logging.*
constants, compute key = log_level.upper(), set numeric_level = mapping.get(key,
logging.INFO) and then call configure_sdk_logger(level=numeric_level); keep the
rest of run (app creation and uvicorn.run) unchanged and validate unknown levels
by falling back to logging.INFO.
In @examples/05_agent_server_example/pyproject.toml:
- Line 4: Update the examples/05_agent_server_example pyproject.toml to relax
the Python requirement: change the requires-python entry (requires-python) from
">=3.13" to ">=3.10" so it aligns with the core vision-agents package and does
not unnecessarily restrict users.
🧹 Nitpick comments (6)
examples/05_agent_server_example/README.md (1)
1-1: Documentation placeholder needs completion.This README is currently a stub. Consider adding documentation that covers:
- Purpose of the example
- Setup instructions
- How to run the agent server
- API endpoints available
- Example usage/requests
Would you like me to generate initial README content for this example, or open an issue to track this documentation task?
examples/05_agent_server_example/agent_server_example.py (3)
14-24: Module docstring placement issue.The module-level docstring should appear at the very top of the file before imports. Currently it's positioned after the imports, which means Python won't recognize it as the module's
__doc__.🔧 Suggested fix
Move the docstring to the top of the file:
+""" +Agent example optimized for fast response time. + +Eager turn taking STT, LLM, TTS workflow +- deepgram for optimal latency +- eleven labs for TTS +- gemini-2.5-flash-lite for fast responses +- stream's edge network for video transport + +This example uses STT, for a realtime openAI/gemini example see 02_golf_coach_example +""" + import logging from typing import Any, Dict ... -""" -Agent example optimized for fast response time. -... -"""
47-48: Function docstring missing.Per Google style guide for docstrings, async functions should have docstrings describing their purpose, args, and return value. As per coding guidelines, docstrings should follow the Google style guide.
📝 Suggested docstring
@llm.register_function(description="Get current weather for a location") async def get_weather(location: str) -> Dict[str, Any]: + """Get current weather for a location. + + Args: + location: The location to get weather for. + + Returns: + Weather data dictionary for the specified location. + """ return await get_weather_by_location(location)
53-64: Good structure for the join_call callback.The TODO comment on lines 54-55 is a valid concern - the coupling to
call_typeandcall_idparameters does tie this to a specific Edge implementation. Consider tracking this as a future improvement.Would you like me to open an issue to track the abstraction of call parameters?
agents-core/vision_agents/core/runners/http/runner.py (1)
42-54: Minor inconsistency in return value.The method uses
appas a local reference but returnsself._fast_api. While functionally equivalent, returningappwould be more consistent.🔧 Suggested fix
def _configure_fastapi_app(self) -> FastAPI: app = self._fast_api app.state.launcher = self._launcher app.include_router(router) # TODO: Allow to control origins app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) - return self._fast_api + return appagents-core/vision_agents/core/agents/agent_launcher.py (1)
144-152: Consider logging when cancelling an agent.Adding a debug log when cancelling would aid troubleshooting.
🔧 Suggested enhancement
async def close_agent(self, agent_id: str, wait: bool = False) -> None: task = self._call_tasks.pop(agent_id, None) if task is None: raise AgentNotFoundError(f"Agent with id {agent_id} not found") + logger.debug(f"Closing agent {agent_id}, wait={wait}") if wait: await cancel_and_wait(task) else: task.cancel()
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
examples/05_agent_server_example/uv.lockis excluded by!**/*.lockuv.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
agents-core/pyproject.tomlagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runners/__init__.pyagents-core/vision_agents/core/runners/http/__init__.pyagents-core/vision_agents/core/runners/http/models.pyagents-core/vision_agents/core/runners/http/routes.pyagents-core/vision_agents/core/runners/http/runner.pyexamples/05_agent_server_example/README.mdexamples/05_agent_server_example/__init__.pyexamples/05_agent_server_example/agent_server_example.pyexamples/05_agent_server_example/pyproject.toml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/runners/http/__init__.pyagents-core/vision_agents/core/runners/http/routes.pyagents-core/vision_agents/core/runners/__init__.pyexamples/05_agent_server_example/__init__.pyagents-core/vision_agents/core/runners/http/runner.pyexamples/05_agent_server_example/agent_server_example.pyagents-core/vision_agents/core/runners/http/models.pyagents-core/vision_agents/core/agents/agent_launcher.py
🧠 Learnings (1)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Use uv for dependency management on this project, not python -m or system package managers
Applied to files:
examples/05_agent_server_example/pyproject.toml
🧬 Code graph analysis (5)
agents-core/vision_agents/core/runners/http/__init__.py (1)
agents-core/vision_agents/core/runners/http/runner.py (1)
HTTPServerRunner(23-54)
agents-core/vision_agents/core/runners/__init__.py (1)
agents-core/vision_agents/core/runners/http/runner.py (1)
HTTPServerRunner(23-54)
agents-core/vision_agents/core/runners/http/runner.py (3)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
AgentLauncher(19-228)agents-core/vision_agents/core/utils/logging.py (1)
configure_sdk_logger(19-46)agents-core/vision_agents/core/edge/sfu_events.py (1)
level(496-500)
agents-core/vision_agents/core/runners/http/models.py (1)
agents-core/vision_agents/core/edge/sfu_events.py (1)
message(662-666)
agents-core/vision_agents/core/agents/agent_launcher.py (2)
agents-core/vision_agents/core/utils/utils.py (2)
cancel_and_wait(105-121)await_or_run(92-102)agents-core/vision_agents/core/agents/agents.py (1)
join(510-588)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Ruff
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Ruff
🔇 Additional comments (12)
examples/05_agent_server_example/__init__.py (1)
1-1: Package initializer is correct.An empty
__init__.pyis the standard pattern for Python packages. This is appropriate for an example package.examples/05_agent_server_example/pyproject.toml (1)
17-22: Editable local dependencies configured correctly.The use of
[tool.uv.sources]with editable paths for local development is appropriate and follows the project's dependency management practices.Based on learnings, this correctly uses uv for dependency management.
agents-core/vision_agents/core/runners/http/__init__.py (1)
1-1: LGTM!The explicit re-export pattern correctly exposes
HTTPServerRunnerat the package level for clean imports.agents-core/vision_agents/core/runners/__init__.py (1)
1-1: LGTM!The re-export makes
HTTPServerRunnerconveniently accessible at the package level, enabling clean imports likefrom vision_agents.core.runners import HTTPServerRunner.agents-core/pyproject.toml (1)
30-30: No issues found. FastAPI 0.128.0 is the latest stable version (released Dec 27, 2025) with no known direct security vulnerabilities. The version constraint is valid.examples/05_agent_server_example/agent_server_example.py (1)
67-69: LGTM!Clean main block wiring HTTPServerRunner with AgentLauncher.
agents-core/vision_agents/core/runners/http/runner.py (2)
32-32: Emptyworkermethod - intentional placeholder?The
workermethod is defined but empty. If this is intentional for future implementation, consider adding a docstring or removing it until needed.
47-53: Permissive CORS configuration acknowledged.The TODO on line 46 acknowledges this needs refinement. For production,
allow_origins=["*"]withallow_credentials=Trueis a security concern. The combination can expose credentials to any origin.Consider prioritizing the CORS configuration before production deployment.
agents-core/vision_agents/core/runners/http/models.py (2)
1-48: Well-structured Pydantic models.The models are cleanly defined with proper Field descriptions and sensible defaults. The use of
str | Noneunion syntax is appropriate for Python 3.10+.
127-142: Models defined but not yet used in routes.
AgentSessionInfoandListAgentsResponseare defined but not currently used by any route inroutes.py. This is fine for a WIP if you plan to add list/status endpoints.Are these models intended for upcoming endpoints?
agents-core/vision_agents/core/agents/agent_launcher.py (1)
126-142: Good implementation of the join method.The
joinmethod properly creates agents, warms them up, tracks tasks, and cleans up via done callback. The UUID-based agent_id provides good uniqueness.agents-core/vision_agents/core/runners/http/routes.py (1)
22-30: Clean lifespan management pattern.The async context manager for launcher lifecycle is well-structured with proper try/finally cleanup.
3e24225 to
f718488
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @agents-core/vision_agents/core/agents/agent_launcher.py:
- Around line 78-82: The loop is iterating over awaitables returned by
asyncio.as_completed(coros) but then calls .done()/.exception() on them without
awaiting; fix by awaiting each item yielded by asyncio.as_completed to get the
completed task/result (await result) before inspecting it, e.g., iterate "async
for coro in asyncio.as_completed(coros): completed = await coro" then check
completed.done()/completed.cancelled()/completed.exception() and log exceptions
from cancel_and_wait; ensure you reference the coros list, cancel_and_wait, and
self._call_tasks to locate and update the code.
- Around line 29-30: The Coroutine type parameters are in the wrong order;
import Any from typing and change Coroutine["Agent", ..., ...] to Coroutine[Any,
Any, "Agent"] (for create_agent) and change Coroutine[None, ..., ...] to
Coroutine[Any, Any, None] (for join_call), keeping the existing Callable
signatures and quoted "Agent" forward reference as-is; also add Any to the
module imports.
🧹 Nitpick comments (4)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
126-142: Implementation looks solid; address the typing TODO.The task lifecycle management with done callback for cleanup is well-structured. Consider addressing the
TODO: Typingcomment before merging—the method's return type annotation is missing.Would you like me to help generate proper type annotations for this method?
examples/05_agent_server_example/README.md (1)
1-1: Address the TODO before merging.This placeholder README should be populated with setup instructions, usage examples, and configuration details for the agent server example.
Would you like me to draft a README with sections for prerequisites, running the example, API endpoints, and configuration options?
examples/05_agent_server_example/pyproject.toml (1)
14-14: Consider movingpyinstrumentto optional dependencies.This profiling tool seems like a development/debugging aid rather than a runtime requirement for the example.
dependencies = [ "python-dotenv>=1.0", "vision-agents-plugins-deepgram", "vision-agents-plugins-elevenlabs", "vision-agents-plugins-getstream", "vision-agents-plugins-gemini", "vision-agents", - "pyinstrument>=5.1.1", ] + +[project.optional-dependencies] +dev = ["pyinstrument>=5.1.1"]agents-core/vision_agents/core/runners/http/runner.py (1)
36-36: Avoid usinggetattrper coding guidelines.The coding guidelines state to avoid
getattr,hasattr,delattr, andsetattr; prefer normal attribute access.✏️ Suggested fix
- numeric_level = getattr(logging, log_level.upper(), logging.INFO) + log_level_map = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, + } + numeric_level = log_level_map.get(log_level.upper(), logging.INFO)Based on coding guidelines.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
examples/05_agent_server_example/uv.lockis excluded by!**/*.lockuv.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
agents-core/pyproject.tomlagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runners/__init__.pyagents-core/vision_agents/core/runners/http/__init__.pyagents-core/vision_agents/core/runners/http/models.pyagents-core/vision_agents/core/runners/http/routes.pyagents-core/vision_agents/core/runners/http/runner.pyexamples/05_agent_server_example/README.mdexamples/05_agent_server_example/__init__.pyexamples/05_agent_server_example/agent_server_example.pyexamples/05_agent_server_example/pyproject.toml
✅ Files skipped from review due to trivial changes (1)
- examples/05_agent_server_example/init.py
🚧 Files skipped from review as they are similar to previous changes (3)
- agents-core/vision_agents/core/runners/init.py
- examples/05_agent_server_example/agent_server_example.py
- agents-core/vision_agents/core/runners/http/routes.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/runners/http/runner.pyagents-core/vision_agents/core/runners/http/__init__.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runners/http/models.py
🧠 Learnings (1)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Use uv for dependency management on this project, not python -m or system package managers
Applied to files:
examples/05_agent_server_example/pyproject.toml
🧬 Code graph analysis (2)
agents-core/vision_agents/core/runners/http/runner.py (2)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
AgentLauncher(19-228)agents-core/vision_agents/core/utils/logging.py (1)
configure_sdk_logger(19-46)
agents-core/vision_agents/core/runners/http/__init__.py (1)
agents-core/vision_agents/core/runners/http/runner.py (1)
HTTPServerRunner(23-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Ruff
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Mypy
🔇 Additional comments (9)
agents-core/vision_agents/core/agents/agent_launcher.py (2)
144-152: LGTM!The
close_agentmethod properly handles both synchronous and awaited cancellation patterns, and raises the appropriate custom exception for unknown agent IDs.
16-17: LGTM!Simple, focused exception class for the specific error case.
examples/05_agent_server_example/pyproject.toml (1)
4-4: Python version requirement is stricter than the core package.The example requires Python
>=3.13whilevision-agentscore supports>=3.10. This may confuse users who can run the core library but not this example.Is the 3.13 requirement intentional (e.g., for a specific language feature), or should it align with the core package?
agents-core/vision_agents/core/runners/http/__init__.py (1)
1-1: LGTM!Clean re-export using the explicit
X as Xpattern for public API exposure.agents-core/vision_agents/core/runners/http/runner.py (1)
32-32: Clarify the purpose of the emptyworkermethod.This stub method has no implementation. Is it intended for future use (background task management, health monitoring), or should it be removed?
agents-core/vision_agents/core/runners/http/models.py (3)
8-47: Well-structured configuration model.The
AgentConfigmodel provides sensible defaults with clear field descriptions. Good use of Pydantic'sFieldfor documentation.
99-101: Ensureuser_idis not logged in request handlers.The
user_idfield is a user identifier that could constitute PII. Verify that request logging (if any) in the HTTP routes redacts or excludes this field to maintain compliance.
127-148: LGTM!The session info and response models are well-defined.
AgentSessionInfoappropriately tracks timestamps for session lifecycle, andErrorResponsefollows standard API error patterns.agents-core/pyproject.toml (1)
30-30: FastAPI 0.128.0 is confirmed available on PyPI with no known vulnerabilities.Adding FastAPI as a core dependency means all vision-agents users will pull it in. Version 0.128.0 exists on PyPI and is the current latest release with no reported CVEs or security advisories.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In @agents-core/vision_agents/core/agents/agent_launcher.py:
- Around line 112-117: The stop() loop incorrectly treats items from
asyncio.as_completed as results; instead create tasks from cancel_and_wait for
each session (e.g., tasks = [asyncio.create_task(cancel_and_wait(s.task)) for s
in self._sessions.values()]) and then iterate: for fut in
asyncio.as_completed(tasks): use try/except around await fut to catch
asyncio.CancelledError (ignore) and catch other Exceptions to log via
logger.error with the exception; avoid calling fut.exception() on possibly
cancelled futures without awaiting them. This uses the existing cancel_and_wait
and self._sessions symbols and preserves correct async error handling.
- Around line 192-201: The session is being popped from self._sessions before
cancelling its task, which can make other code think the session is gone while
it’s still running; in close_session, first retrieve the session (use
self._sessions.get or similar), raise SessionNotFoundError if None, then perform
cancellation (await cancel_and_wait(session.task) if wait else
session.task.cancel()), and only after cancellation completes (or in a finally
block to guarantee cleanup) remove the session from self._sessions via
self._sessions.pop(session_id, None); reference close_session, session.task,
cancel_and_wait, and self._sessions.pop when applying the change.
In @agents-core/vision_agents/core/runner/http/api.py:
- Around line 76-78: The DELETE route declaration using router.delete with path
"/session/{session_id}" is inconsistent with other endpoints that use
"/sessions/"; change the path to "/sessions/{session_id}" in the router.delete
decorator (and update any associated summary or docstrings if needed) so all
endpoints use the plural resource naming; also scan for and update any
references or tests that call the old "/session/{session_id}" route to use
"/sessions/{session_id}" to avoid broken links.
- Line 32: Move the lifespan context manager off APIRouter and attach it to the
FastAPI app (create the app with FastAPI(lifespan=lifespan) instead of passing
lifespan into APIRouter), update router = APIRouter(...) to remove the lifespan
argument; replace the broad except Exception as e in the request/launch handler
with a specific exception type (e.g., except AgentLauncherError as e:) and
import/define that error type used by the launcher; and normalize route paths to
use plural "sessions" consistently (change "/session/{session_id}" to
"/sessions/{session_id}" or change "/sessions/{session_id}/leave" to singular,
ensuring both handlers use the same "/sessions/{session_id}" base).
In @agents-core/vision_agents/core/utils/logging.py:
- Line 7: The unconditional import of AccessFormatter from uvicorn (the
statement importing AccessFormatter) will fail if uvicorn is not installed;
update pyproject.toml to add uvicorn as a dependency or modify the import in
logging.py to try importing AccessFormatter from uvicorn and fall back to a
no-op or local simple formatter when the import raises ImportError, ensuring any
references to AccessFormatter (e.g., in logger setup functions) use the fallback
formatter if uvicorn is absent.
🧹 Nitpick comments (16)
agents-core/vision_agents/core/runner/http/models.py (2)
16-36: Consider consolidatingJoinCallResponseandGetAgentSessionResponse.These two models share identical fields (
session_id,call_id,config,started_at). Consider using a single model or having one inherit from the other to reduce duplication.Additionally, the
config: dicttyping is quite loose. The TODO is noted, but consider defining at least aTypedDictor a proper Pydantic model for better validation and documentation.♻️ Suggested consolidation
-class JoinCallResponse(BaseModel): - """Response after successfully starting an agent.""" - - session_id: str = Field(..., description="The ID of the agent session") - call_id: str = Field(..., description="The ID of the call") - config: dict # TODO: Make it a type - started_at: datetime - -class ErrorResponse(BaseModel): - """Standard error response.""" - - detail: str - - -class GetAgentSessionResponse(BaseModel): - """Information about an active agent session.""" - - session_id: str - call_id: str - config: dict # TODO: Make it a type - started_at: datetime +class AgentSessionResponse(BaseModel): + """Information about an agent session.""" + + session_id: str = Field(..., description="The ID of the agent session") + call_id: str = Field(..., description="The ID of the call") + config: dict = Field(..., description="Agent configuration") # TODO: Make it a typed model + started_at: datetime = Field(..., description="Session start time") + + +# Aliases for semantic clarity in different contexts +JoinCallResponse = AgentSessionResponse +GetAgentSessionResponse = AgentSessionResponse + + +class ErrorResponse(BaseModel): + """Standard error response.""" + + detail: str
23-24: Missing blank line beforeErrorResponseclass.PEP 8 recommends two blank lines before top-level class definitions.
♻️ Formatting fix
started_at: datetime + class ErrorResponse(BaseModel):agents-core/vision_agents/core/cli/cli_runner.py (3)
87-87: Avoidgetattrfor log level lookup per coding guidelines.Replace
getattr(logging, log_level.upper(), logging.INFO)with explicit level mapping.Suggested fix
- numeric_level = getattr(logging, log_level.upper(), logging.INFO) + log_levels = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, + } + numeric_level = log_levels.get(log_level.upper(), logging.INFO)
115-121: Avoidhasattrper coding guidelines.Use direct attribute access with try/except or check via a protocol/interface instead.
Suggested fix
- if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): + if not no_demo: + edge = getattr(agent, "edge", None) + open_demo = getattr(edge, "open_demo_for_agent", None) if edge else None + if callable(open_demo): logger.info("🌐 Opening demo UI...") await agent.edge.open_demo_for_agent(agent, call_type, call_id)Alternatively, define a protocol or interface that guarantees the
edgeattribute exists with the expected method, then type-check accordingly.
131-133: Use specific exception handling instead ofexcept Exception as e.Per coding guidelines, avoid catching the base
Exception. Consider catching more specific exceptions thatstart_sessionandwaitmight raise.examples/05_agent_server_example/agent_server_example.py (1)
11-23: Module docstring placement is unconventional.The module-level docstring appears after
load_dotenv()execution. Move it to the top of the file before imports for standard Python convention.Suggested structure
+""" +Agent example optimized for fast response time. + +Eager turn taking STT, LLM, TTS workflow +- deepgram for optimal latency +- eleven labs for TTS +- gemini-2.5-flash-lite for fast responses +- stream's edge network for video transport + +This example uses STT, for a realtime openAI/gemini example see 02_golf_coach_example +""" + import logging from typing import Any, Dict from dotenv import load_dotenv ... load_dotenv() - -""" -Agent example optimized for fast response time. -... -"""agents-core/vision_agents/core/runner/http/api.py (2)
61-66: Use specific exception handling instead ofexcept Exception as e.Per coding guidelines, catch specific exceptions that
launcher.start_sessionmay raise rather than the broadExceptionclass.
161-171: Readiness check returns 400 for not-ready state.Consider using 503 Service Unavailable instead of 400 Bad Request, as 503 is more semantically appropriate for readiness/health probes.
Suggested fix
if launcher.warmed_up and launcher.running: return Response(status_code=200) else: raise HTTPException( - status_code=400, detail="Server is not ready to accept requests" + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Server is not ready to accept requests" )agents-core/vision_agents/core/runner/runner.py (6)
108-109: Avoidgetattrfor log level lookup per coding guidelines.Same issue as in cli_runner.py - use explicit mapping instead.
Suggested fix
- numeric_level = getattr(logging, log_level.upper(), logging.INFO) + log_levels = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, + } + numeric_level = log_levels.get(log_level.upper(), logging.INFO)
134-140: Avoidhasattrper coding guidelines.Use direct attribute access or protocol-based checking.
150-152: Use specific exception handling instead ofexcept Exception as e.Per coding guidelines, avoid catching base
Exception.
121-155: Significant code duplication withcli_runner.py.The
_run()async function inRunner.run()is nearly identical to the one incli_runner.py. Consider extracting this into a shared utility or havingcli_runner.pydelegate toRunner.
181-196: Incomplete docstring - Args descriptions are empty.The
serve()method docstring lists parameters but provides no descriptions.Suggested fix
""" Start the HTTP server that spawns agents to the calls. Args: - host: - port: - agents_log_level: - http_log_level: - cors_allow_origins: - cors_allow_methods: - cors_allow_headers: - cors_allow_credentials: - - Returns: - + host: Server host address + port: Server port number + agents_log_level: Logging level for agent operations + http_log_level: Logging level for FastAPI/uvicorn + cors_allow_origins: Allowed CORS origins + cors_allow_methods: Allowed CORS methods + cors_allow_headers: Allowed CORS headers + cors_allow_credentials: Whether to allow credentials in CORS """
204-210: Moregetattrusage for log level configuration.Apply the same fix pattern here for consistency.
agents-core/vision_agents/core/agents/agent_launcher.py (2)
26-26: Exception class body is empty - consider adding a pass statement or docstring.The ellipsis works but a docstring would be clearer.
Suggested fix
-class SessionNotFoundError(Exception): ... +class SessionNotFoundError(Exception): + """Raised when a session with the given ID is not found."""
178-182: Done callback signature: first parameter is the task itself.The
_done_cbfunction's first parameter (currently_) receives the completed task. This is fine but documenting it might help future maintainers.Clearer naming
- def _done_cb(_, agent_id_=agent.id): + def _done_cb(task: asyncio.Task, agent_id_: str = agent.id) -> None: self._sessions.pop(agent_id_, None)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
agents-core/vision_agents/core/__init__.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/cli/cli_runner.pyagents-core/vision_agents/core/runner/__init__.pyagents-core/vision_agents/core/runner/http/__init__.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/utils/logging.pyexamples/05_agent_server_example/agent_server_example.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/runner/__init__.pyagents-core/vision_agents/core/cli/cli_runner.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/__init__.pyexamples/05_agent_server_example/agent_server_example.pyagents-core/vision_agents/core/utils/logging.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/agents/agent_launcher.py
🧬 Code graph analysis (4)
agents-core/vision_agents/core/cli/cli_runner.py (3)
agents-core/vision_agents/core/vad/silero.py (1)
session(53-66)agents-core/vision_agents/core/agents/agent_launcher.py (2)
start_session(164-190)wait(45-49)plugins/aws/vision_agents/plugins/aws/aws_realtime.py (1)
start_session(500-505)
agents-core/vision_agents/core/agents/agents.py (1)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
id(42-43)
agents-core/vision_agents/core/__init__.py (4)
agents-core/vision_agents/core/cli/cli_runner.py (1)
cli(25-152)agents-core/vision_agents/core/runner/runner.py (2)
cli(244-358)Runner(41-358)dev.py (1)
cli(50-53)agents-core/vision_agents/core/edge/types.py (1)
User(15-18)
agents-core/vision_agents/core/agents/agent_launcher.py (3)
agents-core/vision_agents/core/agents/agents.py (2)
id(258-259)set_video_track_override_path(907-915)agents-core/vision_agents/core/runner/http/api.py (2)
join_call(52-73)close_session(80-96)agents-core/vision_agents/core/utils/utils.py (2)
cancel_and_wait(105-121)await_or_run(92-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Mypy
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Ruff
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
🔇 Additional comments (14)
agents-core/vision_agents/core/agents/agents.py (1)
150-150: LGTM! Clean implementation of the agent identity property.The
_idfield initialized withuuid4()and the read-onlyidproperty provide a stable public identifier for the agent. This aligns well with theAgentSession.idusage inagent_launcher.pythat delegates toself.agent.id.Also applies to: 257-260
agents-core/vision_agents/core/utils/logging.py (2)
20-34: LGTM! Clean extraction of formatter creation logic.The
_get_colored_formatterhelper reduces duplication and provides a consistent way to create colored formatters with the expanded color mapping (now including WARNING and ERROR levels).
59-96: LGTM! Well-structured FastAPI logger configuration.The function follows the same patterns as
configure_sdk_logger, maintaining consistency. The separation ofAccessFormatterfor access logs and generic colored formatter for other loggers is appropriate.agents-core/vision_agents/core/runner/__init__.py (1)
1-1: LGTM! Proper re-export pattern.The
as Runnersyntax correctly signals to type checkers thatRunneris intentionally part of the public API.agents-core/vision_agents/core/__init__.py (1)
1-13: LGTM! Public API surface properly extended.The
Runnerclass is now correctly exported alongsideAgent,User,cli, andAgentLauncher. The__all__list accurately reflects the module's public API.agents-core/vision_agents/core/cli/cli_runner.py (1)
124-128: Session-based workflow looks good.The transition to
launcher.start_session()andsession.wait()cleanly integrates with the new session lifecycle API.examples/05_agent_server_example/agent_server_example.py (3)
26-49: Agent creation function looks well-structured.The
create_agentfunction properly configures the Agent with all required components and registers the MCP function correctly.
52-61: Consider adding error handling around call operations.The
join_callfunction has no exception handling. Ifagent.create_call,agent.join,agent.simple_response, oragent.finishfail, exceptions will propagate unhandled.Is error handling intentionally delegated to the caller (Runner/AgentLauncher), or should this function handle specific failures gracefully?
64-67: Clean entry point using the new Runner API.The wiring of
AgentLauncherwithRunner.cli()is straightforward and demonstrates the intended usage pattern.agents-core/vision_agents/core/runner/http/api.py (2)
45-73: Endpoint implementation is solid.The
join_callendpoint correctly usesstart_sessionand returns appropriate response data with proper status codes.
113-120: Beacon endpoint gracefully handles missing sessions.Correctly returns 200 even when session not found, as the agent may have already been cleaned up. Good defensive design for sendBeacon semantics.
agents-core/vision_agents/core/runner/runner.py (1)
219-242: FastAPI app configuration is well-structured.The
_configure_fastapi_appmethod properly handles the bypass case, attaches state, and configures CORS middleware correctly.agents-core/vision_agents/core/agents/agent_launcher.py (2)
29-50: Well-designedAgentSessiondataclass.Clean encapsulation of session state with useful
finishedandidproperties, and asyncwait()method.
164-190: Session lifecycle management is well-implemented.The
start_sessionmethod correctly:
- Launches the agent
- Sets video track override if provided
- Creates a named task for the join_call
- Registers cleanup callback via
add_done_callback- Stores session in
_sessionsdict
e4a1ac9 to
fac9abe
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/runner/runner.py`:
- Around line 148-152: The catch-all "except Exception as e" in runner.py should
be replaced with explicit exception handlers for the known errors the agent
launch/session code can raise (for example RuntimeError, OSError,
asyncio.CancelledError or your project's AgentLaunchError/SessionError types)
instead of catching Exception; update the block around the logger.error call to
handle each specific exception type (logging with exc_info=True and performing
any specific cleanup or re-raise logic per case), and leave any truly unexpected
errors to propagate (or catch them in a top-level handler that records critical
diagnostics) rather than using a bare except Exception.
♻️ Duplicate comments (7)
examples/05_agent_server_example/pyproject.toml (1)
4-4: Python version requirement is overly restrictive.This has been previously flagged:
requires-python = ">=3.13"should be relaxed to">=3.10"to align with the corevision-agentspackage.agents-core/vision_agents/core/utils/logging.py (1)
7-7: Unconditionaluvicornimport may cause import failures.The import of
AccessFormatterfromuvicorn.loggingwill fail for users who install the package without uvicorn. Either adduvicornto required dependencies inpyproject.toml, or wrap this import in a try/except with a fallback.agents-core/vision_agents/core/runner/http/api.py (3)
32-32: Router-levellifespanis not executed when included in a FastAPI app.The
lifespanparameter onAPIRouteris documented but not invoked when the router is included viaapp.include_router(). Move the lifespan context manager to theFastAPI()app initialization instead.
61-66: Avoid catching bareException.Per coding guidelines, never write
except Exception as e. Identify specific exceptions thatlauncher.start_sessionmight raise (e.g.,RuntimeError,ConnectionError) and handle them explicitly, or document why a broad catch is necessary for this WIP stage.
76-78: Inconsistent URL path naming:/session/vs/sessions/.This DELETE endpoint uses singular
/session/{session_id}while other endpoints use plural/sessions/. This inconsistency may confuse API consumers.♻️ Suggested fix
`@router.delete`( - "/session/{session_id}", + "/sessions/{session_id}", summary="Close the agent session and remove it from call", )agents-core/vision_agents/core/agents/agent_launcher.py (2)
192-201: Session removed before task cancellation completes.The session is popped from
_sessionsbefore the task is cancelled. If cancellation is slow or fails, other code may assume the session is gone while its task is still running. Consider cancelling first, then removing.♻️ Suggested reordering
async def close_session(self, session_id: str, wait: bool = False) -> None: - session = self._sessions.pop(session_id, None) + session = self._sessions.get(session_id) if session is None: raise SessionNotFoundError(f"Session with id {session_id} not found") if wait: await cancel_and_wait(session.task) else: session.task.cancel() + + self._sessions.pop(session_id, None)
112-117: Bug:asyncio.as_completedyields awaitables, not completed tasks.The loop iterates over awaitables from
as_completedbut calls.done()and.exception()on them without awaiting first. Afteras_completedyields, you mustawaitthe result to get the actual completed task/result.🐛 Proposed fix using asyncio.gather
coros = [cancel_and_wait(s.task) for s in self._sessions.values()] - async for result in cast(AsyncIterator[Task], asyncio.as_completed(coros)): - if result.done() and not result.cancelled() and result.exception(): - logger.error(f"Failed to cancel the agent task: {result.exception()}") + results = await asyncio.gather(*coros, return_exceptions=True) + for result in results: + if isinstance(result, Exception) and not isinstance(result, asyncio.CancelledError): + logger.error(f"Failed to cancel the agent task: {result}")
🧹 Nitpick comments (1)
agents-core/vision_agents/core/runner/runner.py (1)
134-140: Avoidhasattrchecks; prefer explicit type checks or try/except.Per coding guidelines, avoid using
hasattr. Consider using a protocol/interface check, or handleAttributeErrorexplicitly if the edge type is uncertain.♻️ Suggested approach using try/except
# Open demo UI by default - if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) + if not no_demo: + try: + logger.info("🌐 Opening demo UI...") + await agent.edge.open_demo_for_agent(agent, call_type, call_id) + except AttributeError: + logger.debug("Edge does not support open_demo_for_agent")
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
examples/05_agent_server_example/uv.lockis excluded by!**/*.lockuv.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
agents-core/pyproject.tomlagents-core/vision_agents/core/__init__.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/cli/cli_runner.pyagents-core/vision_agents/core/runner/__init__.pyagents-core/vision_agents/core/runner/http/__init__.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/utils/logging.pyexamples/05_agent_server_example/README.mdexamples/05_agent_server_example/__init__.pyexamples/05_agent_server_example/agent_server_example.pyexamples/05_agent_server_example/pyproject.toml
✅ Files skipped from review due to trivial changes (1)
- examples/05_agent_server_example/init.py
🚧 Files skipped from review as they are similar to previous changes (6)
- examples/05_agent_server_example/README.md
- agents-core/vision_agents/core/runner/http/models.py
- agents-core/vision_agents/core/cli/cli_runner.py
- examples/05_agent_server_example/agent_server_example.py
- agents-core/vision_agents/core/init.py
- agents-core/pyproject.toml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/__init__.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/utils/logging.py
🧠 Learnings (1)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Use uv for dependency management on this project, not python -m or system package managers
Applied to files:
examples/05_agent_server_example/pyproject.toml
🧬 Code graph analysis (5)
agents-core/vision_agents/core/runner/http/api.py (2)
agents-core/vision_agents/core/agents/agent_launcher.py (8)
SessionNotFoundError(26-26)start(98-105)start_session(164-190)id(42-43)close_session(192-201)get_session(203-208)warmed_up(142-143)running(146-147)agents-core/vision_agents/core/runner/http/models.py (3)
GetAgentSessionResponse(30-36)JoinCallRequest(8-13)JoinCallResponse(16-22)
agents-core/vision_agents/core/runner/__init__.py (1)
agents-core/vision_agents/core/runner/runner.py (1)
Runner(41-358)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
agents-core/vision_agents/core/runner/http/api.py (2)
join_call(52-73)close_session(80-96)
agents-core/vision_agents/core/agents/agents.py (1)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
id(42-43)
agents-core/vision_agents/core/runner/runner.py (2)
agents-core/vision_agents/core/agents/agent_launcher.py (6)
AgentLauncher(53-284)start(98-105)launch(149-162)start_session(164-190)wait(45-49)stop(107-118)plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py (1)
open_demo_for_agent(428-434)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Ruff
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Test "not integration"
🔇 Additional comments (19)
examples/05_agent_server_example/pyproject.toml (2)
7-15: Dependencies look appropriate for the example project.The dependency set covers:
- Environment configuration (
python-dotenv)- Required vision-agents plugins and core
- Profiling capability (
pyinstrument)This aligns well with an HTTP agent server demonstration.
17-22: Editable sources correctly configured for monorepo development.The
[tool.uv.sources]section properly references local packages via relative paths witheditable = true, enabling seamless local development. This follows the project's convention of usinguvfor dependency management, as per learnings.agents-core/vision_agents/core/agents/agents.py (2)
151-151: LGTM!The UUID-based agent identifier is properly initialized during construction, providing a stable identity for session management throughout the agent lifecycle.
259-262: LGTM!The read-only property cleanly exposes the internal
_idfield, aligning with theAgentSession.idproperty inagent_launcher.pythat delegates toself.agent.id.agents-core/vision_agents/core/utils/logging.py (3)
20-34: LGTM!The extracted
_get_colored_formatterhelper consolidates formatter creation with a sensible color mapping, reducing duplication between SDK and FastAPI logger configuration.
36-56: LGTM!The refactored
configure_sdk_loggerproperly delegates to the new helper while preserving the existing handler-check and level-guard behavior.
59-96: LGTM!The new
configure_fastapi_loggersfunction provides consistent formatting for FastAPI/Uvicorn logs, with appropriate access log formatting that includes client address and request details.agents-core/vision_agents/core/agents/agent_launcher.py (4)
26-50: LGTM!The
SessionNotFoundErrorandAgentSessiondataclass provide a clean abstraction for session lifecycle management. Thefinishedproperty andwait()method offer convenient ways to check and await session completion.
141-147: LGTM!The
warmed_upandrunningproperties provide clean read-only access to launcher state, used by the HTTP API's/readyendpoint.
164-190: LGTM!The
start_sessionmethod orchestrates agent launch, optional video override, and task scheduling with proper cleanup callbacks. The closure capturingagent_id_via default argument is a solid pattern to avoid late-binding issues.
203-208: LGTM!The
get_sessionmethod provides straightforward lookup with appropriate error handling.agents-core/vision_agents/core/runner/http/api.py (4)
21-29: LGTM!The lifespan context manager properly handles launcher lifecycle with try/finally ensuring
stop()is called even on errors.
45-73: LGTM!The
join_callendpoint correctly creates sessions and returns appropriate response data. The 201 status code is appropriate for resource creation.
99-120: LGTM!The beacon endpoint gracefully handles already-cleaned-up sessions by returning success, which is appropriate for fire-and-forget
sendBeaconrequests.
153-171: LGTM!The health and readiness endpoints provide essential observability. The readiness check correctly validates both
warmed_upandrunningstates before accepting requests.agents-core/vision_agents/core/runner/runner.py (3)
63-82: LGTM!The
Runner.__init__provides flexible configuration with sensible defaults. The_fast_api_bypassflag enables advanced users to supply fully customized FastAPI instances.
170-217: LGTM!The
servemethod properly configures CORS, logging, and uvicorn withlog_config=Noneto prevent double-logging conflicts with the custom logger configuration.
244-358: LGTM!The CLI implementation with Click provides a clean interface for both
runandservecommands, with comprehensive options for debugging and configuration.agents-core/vision_agents/core/runner/__init__.py (1)
1-1: LGTM!The
Runner as Runnerpattern is a clean, explicit re-export—type checkers and IDEs will thank you for the clarity. This aligns well with the package-level exposure described in the summary.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/runner/http/api.py`:
- Around line 155-166: Update the OpenAPI summary and the function docstring for
get_session_metrics to accurately describe that this endpoint returns metrics
for a running agent session (e.g., "Get metrics for a running agent session" or
similar); modify the summary parameter on the `@router.get` decorator and replace
the current docstring text in the get_session_metrics function to mention
metrics rather than general session info.
♻️ Duplicate comments (5)
agents-core/vision_agents/core/agents/agent_launcher.py (2)
193-203: Session removed before cancellation completes.Popping the session from
_sessionsbefore cancellation finishes may lead to orphaned running tasks if cancellation is slow or fails. Other code might assume the session doesn't exist while it's still active.♻️ Suggested improvement - cancel first, then remove
async def close_session(self, session_id: str, wait: bool = False) -> None: - session = self._sessions.pop(session_id, None) + session = self._sessions.get(session_id) if session is None: raise SessionNotFoundError(f"Session with id {session_id} not found") logger.info(f"Closing agent session with id {session.id}") if wait: await cancel_and_wait(session.task) else: session.task.cancel() + # Remove after cancellation initiated + self._sessions.pop(session_id, None)
112-117:asyncio.as_completedyields awaitables, not completed results.The items yielded by
asyncio.as_completedmust be awaited before inspecting. Callingresult.done()andresult.exception()on an unawaited coroutine won't behave as expected.🐛 Proposed fix
- coros = [cancel_and_wait(s.task) for s in self._sessions.values()] - async for result in cast(AsyncIterator[Task], asyncio.as_completed(coros)): - if result.done() and not result.cancelled() and result.exception(): - logger.error(f"Failed to cancel the agent task: {result.exception()}") + coros = [cancel_and_wait(s.task) for s in self._sessions.values()] + results = await asyncio.gather(*coros, return_exceptions=True) + for result in results: + if isinstance(result, Exception) and not isinstance(result, asyncio.CancelledError): + logger.error(f"Failed to cancel the agent task: {result}")agents-core/vision_agents/core/runner/http/api.py (3)
34-34:lifespanonAPIRouterwill not execute when router is mounted.FastAPI only invokes lifespan context managers defined on the main
FastAPI()app, not on included routers. Move the lifespan to the app initialization in your runner/main module.
63-68: Avoid catching bareException.Per coding guidelines, never write
except Exception as e. Identify the specific exceptions thatlauncher.start_sessionmay raise and handle them explicitly.
78-80: Inconsistent URL path:/session/vs/sessions/.The DELETE endpoint uses singular
/session/{session_id}while all other endpoints use plural/sessions/. Standardize to/sessions/{session_id}for consistency.
🧹 Nitpick comments (3)
agents-core/vision_agents/core/runner/http/models.py (1)
40-42: Docstring appears to be copy-pasted.The docstring "Information about an active agent session" is identical to
GetAgentSessionResponse. Consider updating it to reflect this model's specific purpose, e.g., "Metrics information for an active agent session."agents-core/vision_agents/core/agents/agent_launcher.py (1)
63-64: IncompleteCoroutinetype hint.The
join_callparameter usesCoroutinewithout type parameters. For completeness and type checker compatibility:- join_call: Callable[["Agent", str, str], Coroutine], + join_call: Callable[["Agent", str, str], Coroutine[Any, Any, None]],agents-core/vision_agents/core/runner/http/api.py (1)
206-216: UseAgentLauncherDependencyfor consistency.Other endpoints use the pre-defined
AgentLauncherDependency, but this one usesDepends(_get_launcher)directly. Consider using the alias for consistency.Additionally, HTTP 503 (Service Unavailable) is often more semantically appropriate for readiness probe failures than 400 (Bad Request).
🔧 Suggested fix
`@router.get`("/ready") -async def ready(launcher: AgentLauncher = Depends(_get_launcher)) -> Response: +async def ready(launcher: AgentLauncher = AgentLauncherDependency) -> Response: """ Check if the server is ready to spawn new agents. """ if launcher.warmed_up and launcher.running: return Response(status_code=200) else: raise HTTPException( - status_code=400, detail="Server is not ready to accept requests" + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Server is not ready to accept requests", )
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
agents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/observability/agent.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pytests/test_observability.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/observability/agent.pytests/test_observability.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/agents/agent_launcher.py
**/*test*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*test*.py: Never mock in tests; use pytest for testing
Mark integration tests with@pytest.mark.integrationdecorator
@pytest.mark.asynciois not needed - it is automatic
Files:
tests/test_observability.py
🧬 Code graph analysis (3)
agents-core/vision_agents/core/agents/agents.py (2)
agents-core/vision_agents/core/observability/collector.py (1)
MetricsCollector(51-529)agents-core/vision_agents/core/agents/agent_launcher.py (1)
id(42-43)
tests/test_observability.py (1)
agents-core/vision_agents/core/observability/agent.py (2)
AgentMetrics(52-169)to_dict(149-169)
agents-core/vision_agents/core/agents/agent_launcher.py (3)
agents-core/vision_agents/core/agents/agents.py (2)
id(262-263)set_video_track_override_path(911-919)agents-core/vision_agents/core/runner/http/api.py (2)
join_call(54-75)close_session(82-98)agents-core/vision_agents/core/utils/utils.py (2)
cancel_and_wait(105-121)await_or_run(92-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Mypy
- GitHub Check: unit / Test "not integration"
🔇 Additional comments (13)
agents-core/vision_agents/core/observability/agent.py (1)
149-169: LGTM! Clean implementation for metric serialization.The method correctly converts the dataclass fields to a dictionary of metric values. The use of
dataclasses.asdict()preserves the_Metricinstances (since they're not dataclasses themselves), allowing the subsequent.value()calls to work correctly.The docstring follows Google style guide conventions.
tests/test_observability.py (1)
421-437: LGTM! Good test coverage for the newto_dictmethod.The tests appropriately cover:
- Extracting all fields (default behavior)
- Extracting a subset of fields
- Error handling for unknown fields
These tests avoid mocking and test the actual implementation directly, which aligns well with testing best practices. As per coding guidelines, no mocking is used here.
agents-core/vision_agents/core/agents/agents.py (2)
152-152: LGTM! Agent instance identifier initialization.The unique
_idprovides a stable identifier for session tracking, distinct from the mutableagent_user.id. Good design choice.
259-263: LGTM! MetricsCollector and id property.The
MetricsCollectorinitialization enables per-agent metrics collection via event subscriptions. Theidproperty correctly exposes the internal_idfield as read-only.agents-core/vision_agents/core/agents/agent_launcher.py (3)
26-26: LGTM! Clean exception definition.A dedicated exception for session lookup failures provides clear error semantics.
29-50: LGTM! Well-structured session dataclass.The
AgentSessiondataclass cleanly encapsulates session state with appropriate properties for checking completion status and exposing the agent's id.
164-191: LGTM! Session lifecycle management.The
start_sessionmethod properly:
- Launches the agent
- Sets up video track override if provided
- Creates a managed task with cleanup callback
- Tracks the session in
_sessionsThe done callback correctly captures
agent.idat definition time.agents-core/vision_agents/core/runner/http/models.py (1)
11-13: Remove or utilize theuser_idfield.The
user_idfield is defined as required inJoinCallRequest(line 13) but is not passed tolauncher.start_session()in the API handler (lines 53-74 ofapi.py). The field does not appear to be used elsewhere in the HTTP module. Either remove this field if it's not needed, or pass it to the launcher if it's required for session initialization.agents-core/vision_agents/core/runner/http/api.py (5)
1-21: LGTM!Imports are well-organized and the
__all__export is appropriately scoped to expose only the router.
37-44: LGTM!Clean dependency injection pattern with a reusable
AgentLauncherDependency.
101-122: LGTM!The beacon endpoint correctly handles the fire-and-forget semantics by returning success even when the session is already gone. The warning log is appropriate for observability.
125-152: LGTM!Clean implementation with proper specific exception handling for
SessionNotFoundError.
198-203: LGTM!Simple and effective health check endpoint.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
ed04598 to
3674e7e
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
agents-core/vision_agents/core/cli/cli_runner.py (1)
103-128:start_sessionalready launches an agent—current flow spawns two.You warm an agent and optionally open the demo UI on it, then
start_session()creates a second agent to actually join the call.🐛 Suggested fix
- # Create the agent - agent = await launcher.launch() - if video_track_override: - agent.set_video_track_override_path(video_track_override) - - logger.info("✅ Agent warmed up and ready") - - # Open demo UI by default - if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) - - # Join call if join_call function is provided logger.info(f"📞 Joining call: {call_type}/{call_id}") session = await launcher.start_session( call_id, call_type, video_track_override_path=video_track_override ) + agent = session.agent + + logger.info("✅ Agent warmed up and ready") + + # Open demo UI by default + if ( + not no_demo + and hasattr(agent, "edge") + and hasattr(agent.edge, "open_demo_for_agent") + ): + logger.info("🌐 Opening demo UI...") + await agent.edge.open_demo_for_agent(agent, call_type, call_id) + await session.wait()
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/runner/runner.py`:
- Around line 125-147: The code currently calls await self._launcher.launch() to
create an agent and then calls await self._launcher.start_session(...), but
start_session already creates/launches its own agent, causing a double-spawn;
remove the explicit launch and use the agent returned by start_session (or
modify start_session to accept an existing agent) so the warmed/demo agent is
the one in-session. Concretely: keep await self._launcher.start() for warm-up,
delete/skip await self._launcher.launch(), call session = await
self._launcher.start_session(call_id, call_type, video_track_override_path=...),
then obtain the actual agent from the session (e.g. session.agent or
session.get_agent()) and call open_demo_for_agent on that agent (or update
start_session signature to accept the pre-warmed agent if needed); ensure
references to agent.edge.open_demo_for_agent and later session.wait() use the
session-provided agent.
In `@examples/05_agent_server_example/README.md`:
- Line 1: Replace the "TODO" in examples/05_agent_server_example/README.md with
a minimal usage README that covers: a short setup section (e.g., install
dependencies with npm/yarn and build steps), a clear list of required
environment variables with example values (names and purpose) for the agent
server in this example (mention the example directory name
05_agent_server_example and any expected keys like API keys, PORT, or DB_URL),
and a basic run command that users can copy (e.g., how to start the example with
npm run start or node <entry-file> and how to set env vars inline). Keep it
concise so someone can clone the repo, set the env vars, install deps, and run
the example.
♻️ Duplicate comments (8)
examples/05_agent_server_example/pyproject.toml (1)
4-4: Relax the Python requirement to match core package support.Line 4 likely over-restricts the example; align it with the core package’s supported range.
♻️ Proposed change
-requires-python = ">=3.13" +requires-python = ">=3.10"agents-core/vision_agents/core/agents/agent_launcher.py (2)
107-116:stop()misusesasyncio.as_completed.
as_completedreturns awaitables from a sync iterator; the currentasync for+.done()path won’t work as intended and can skip cancellation failures.🐛 Proposed fix
- coros = [cancel_and_wait(s.task) for s in self._sessions.values()] - async for result in cast(AsyncIterator[Task], asyncio.as_completed(coros)): - if result.done() and not result.cancelled() and result.exception(): - logger.error(f"Failed to cancel the agent task: {result.exception()}") + coros = [cancel_and_wait(s.task) for s in self._sessions.values()] + results = await asyncio.gather(*coros, return_exceptions=True) + for result in results: + if isinstance(result, Exception) and not isinstance( + result, asyncio.CancelledError + ): + logger.error(f"Failed to cancel the agent task: {result}")
193-203: Cancel before removing session from registry.Popping first can leave a running task untracked if cancellation is slow or fails.
🛠️ Suggested adjustment
- session = self._sessions.pop(session_id, None) + session = self._sessions.get(session_id) if session is None: raise SessionNotFoundError(f"Session with id {session_id} not found") logger.info(f"Closing agent session with id {session.id}") - if wait: - await cancel_and_wait(session.task) - else: - session.task.cancel() + try: + if wait: + await cancel_and_wait(session.task) + else: + session.task.cancel() + finally: + self._sessions.pop(session_id, None)agents-core/vision_agents/core/runner/http/api.py (4)
34-34: Router lifespan won’t run when mounted.
APIRouter(lifespan=...)isn’t executed when included into a FastAPI app; move lifespan to the app.🛠️ Minimal change here
-router = APIRouter(lifespan=lifespan) +router = APIRouter()
59-68: Avoid broadexcept Exceptionin join_call.Catch specific launcher/session errors or let FastAPI’s default 500 handler surface unexpected failures. As per coding guidelines, ...
78-80: Use consistent plural resource path.🛠️ Suggested fix
`@router.delete`( - "/session/{session_id}", + "/sessions/{session_id}", summary="Close the agent session and remove it from call", )
155-166: Metrics endpoint summary/docstring mismatch.📝 Suggested fix
`@router.get`( "/sessions/{session_id}/metrics", response_model=GetAgentSessionMetricsResponse, - summary="Get info about a running agent session", + summary="Get metrics for a running agent session", ) @@ - """ - Get info about a running agent session. - """ + """Get metrics for a running agent session."""agents-core/vision_agents/core/runner/runner.py (1)
150-152: Don’t catch bareExceptionin run().Prefer explicit exception types or let unexpected errors propagate. Based on learnings, ...
🧹 Nitpick comments (3)
agents-core/vision_agents/core/runner/http/models.py (1)
3-6: Typeconfigpayloads for clearer API contracts.Untyped
dictweakens OpenAPI and validation. Consider a typed mapping (evendict[str, Any]) plus a Field description.♻️ Suggested refactor
from datetime import datetime +from typing import Any from pydantic import BaseModel, Field @@ class JoinCallResponse(BaseModel): @@ - config: dict # TODO: Make it a type + config: dict[str, Any] = Field(..., description="Agent configuration") @@ class GetAgentSessionResponse(BaseModel): @@ - config: dict # TODO: Make it a type + config: dict[str, Any] = Field(..., description="Agent configuration")Also applies to: 19-22, 34-37
agents-core/vision_agents/core/runner/runner.py (2)
20-21: Replacegetattrlog-level lookup with explicit mapping.Avoid dynamic attribute access; a small map keeps it explicit and typed. As per coding guidelines, ...
♻️ Suggested refactor
logger = logging.getLogger(__name__) +LOG_LEVELS = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, +} @@ - numeric_level = getattr(logging, log_level.upper(), logging.INFO) + numeric_level = LOG_LEVELS.get(log_level.upper(), logging.INFO) @@ - configure_sdk_logger( - level=getattr(logging, agents_log_level.upper(), logging.INFO) - ) + configure_sdk_logger( + level=LOG_LEVELS.get(agents_log_level.upper(), logging.INFO) + ) configure_fastapi_loggers( - level=getattr(logging, http_log_level.upper(), logging.INFO) + level=LOG_LEVELS.get(http_log_level.upper(), logging.INFO) )Also applies to: 108-109, 205-209
134-138: Avoidhasattrchecks; use EAFP access instead.This keeps the control flow explicit and aligns with the no-
hasattrguideline. As per coding guidelines, ...♻️ Suggested refactor
- if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) + if not no_demo: + try: + logger.info("🌐 Opening demo UI...") + await agent.edge.open_demo_for_agent(agent, call_type, call_id) + except AttributeError: + pass
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
examples/05_agent_server_example/uv.lockis excluded by!**/*.lockuv.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
agents-core/pyproject.tomlagents-core/vision_agents/core/__init__.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/agents/agents.pyagents-core/vision_agents/core/cli/cli_runner.pyagents-core/vision_agents/core/observability/agent.pyagents-core/vision_agents/core/runner/__init__.pyagents-core/vision_agents/core/runner/http/__init__.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/utils/logging.pyexamples/05_agent_server_example/README.mdexamples/05_agent_server_example/__init__.pyexamples/05_agent_server_example/agent_server_example.pyexamples/05_agent_server_example/pyproject.tomltests/test_observability.py
✅ Files skipped from review due to trivial changes (1)
- examples/05_agent_server_example/init.py
🚧 Files skipped from review as they are similar to previous changes (7)
- agents-core/vision_agents/core/init.py
- agents-core/vision_agents/core/observability/agent.py
- agents-core/vision_agents/core/agents/agents.py
- examples/05_agent_server_example/agent_server_example.py
- agents-core/pyproject.toml
- agents-core/vision_agents/core/runner/init.py
- agents-core/vision_agents/core/utils/logging.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/cli/cli_runner.pyagents-core/vision_agents/core/runner/runner.pytests/test_observability.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/agents/agent_launcher.py
**/*test*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*test*.py: Never mock in tests; use pytest for testing
Mark integration tests with@pytest.mark.integrationdecorator
@pytest.mark.asynciois not needed - it is automatic
Files:
tests/test_observability.py
🧠 Learnings (2)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Use uv for dependency management on this project, not python -m or system package managers
Applied to files:
examples/05_agent_server_example/pyproject.toml
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*.py : Never write `except Exception as e` - use specific exception handling
Applied to files:
agents-core/vision_agents/core/runner/runner.py
🧬 Code graph analysis (5)
agents-core/vision_agents/core/cli/cli_runner.py (1)
agents-core/vision_agents/core/agents/agent_launcher.py (2)
start_session(164-191)wait(45-49)
tests/test_observability.py (1)
agents-core/vision_agents/core/observability/agent.py (2)
AgentMetrics(52-169)to_dict(149-169)
agents-core/vision_agents/core/runner/http/models.py (2)
agents-core/vision_agents/core/observability/agent.py (1)
description(12-13)agents-core/vision_agents/core/agents/agents.py (1)
metrics(1403-1404)
agents-core/vision_agents/core/runner/http/api.py (1)
agents-core/vision_agents/core/agents/agent_launcher.py (5)
SessionNotFoundError(26-26)id(42-43)close_session(193-203)get_session(205-210)running(146-147)
agents-core/vision_agents/core/agents/agent_launcher.py (2)
agents-core/vision_agents/core/runner/http/api.py (2)
join_call(54-75)close_session(82-98)agents-core/vision_agents/core/utils/utils.py (2)
cancel_and_wait(105-121)await_or_run(92-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Mypy
- GitHub Check: unit / Mypy
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Ruff
🔇 Additional comments (9)
tests/test_observability.py (4)
8-8: LGTM!Standard library import added to support
dataclasses.fields()usage in the new test class.
422-426: LGTM!Test correctly verifies that
to_dict()returns all dataclass fields as keys. The use ofdataclasses.fields()to dynamically get expected field names ensures the test stays in sync with the implementation.
428-432: LGTM!Test correctly verifies that the
fieldsparameter filters the output to only the specified metrics.
434-437: LGTM!Test correctly verifies that
ValueErroris raised with the expected message when an unknown field is requested.agents-core/vision_agents/core/runner/http/models.py (3)
8-13: JoinCallRequest schema looks solid.Clear required fields and sensible defaults.
25-28: ErrorResponse is concise and consistent.
40-53: Metrics response schema is well-structured.agents-core/vision_agents/core/agents/agent_launcher.py (2)
29-49: AgentSession wrapper is clean and minimal.
164-191: Session start lifecycle is coherent.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/agents/agent_launcher.py`:
- Around line 196-207: The close_session method currently pops from
self._sessions and silently returns when the session is missing; change it to
raise SessionNotFoundError instead of returning so callers (e.g., the HTTP
handler expecting a 404) get the correct error. Specifically, in
agent_launcher.py inside close_session(session_id: str, wait: bool = False),
replace the early return on session is None with raising
SessionNotFoundError(session_id) (or the project’s SessionNotFoundError
constructor), and keep the existing logging and cancel/wait logic for existing
sessions (using self._sessions, session.task, and session.id as already
referenced).
In `@agents-core/vision_agents/core/runner/runner.py`:
- Around line 70-77: The Runner class docstring is stale: the __init__ Args
lists a non-existent run_options parameter; update the Runner.__init__ docstring
to match the actual constructor signature (remove the run_options entry and any
references to it) and ensure the Args section only documents the real parameters
(e.g., launcher and serve_options) and their types/behavior so the docstring for
Runner.__init__ is accurate.
- Around line 211-214: In _create_fastapi_app, app.state.options is incorrectly
set from self._serve_options instead of the method parameter; update the
assignment in the _create_fastapi_app function to use the local options
parameter (replace uses of self._serve_options when setting app.state.options)
so the FastAPI app receives the passed-in ServeOptions instance and not the
instance variable.
♻️ Duplicate comments (6)
agents-core/vision_agents/core/runner/runner.py (2)
157-159: Avoid catching bareException.Per coding guidelines, use specific exception handling. Identify the exceptions that
launcher.start_sessionandlauncher.launchmay raise (e.g.,RuntimeError,asyncio.CancelledError, or custom exceptions) and handle them explicitly.
132-154:start_sessionalready creates an agent—avoid double-spawn.The flow calls
self._launcher.launch()(line 136), thenself._launcher.start_session()(line 151), which internally launches another agent. The warmed/demo agent is discarded. Use the session's agent for demo UI instead.agents-core/vision_agents/core/runner/http/api.py (3)
66-71: Avoid catching bareException.Per coding guidelines, use specific exception handling. Catch the specific exceptions that
launcher.start_sessionmay raise.
81-84: Inconsistent URL path:/session/vs/sessions/.The DELETE endpoint uses singular
/session/{session_id}while all other session endpoints use plural/sessions/. Standardize to/sessions/{session_id}for consistency.📝 Suggested fix
`@router.delete`( - "/session/{session_id}", + "/sessions/{session_id}", summary="Close the agent session and remove it from call", dependencies=[Depends(can_close_session)], )
159-171: Summary and docstring don't mention metrics.Both the
summaryand docstring describe "Get info about a running agent session" rather than metrics. Update to accurately describe the endpoint's purpose.📝 Suggested fix
`@router.get`( "/sessions/{session_id}/metrics", response_model=GetAgentSessionMetricsResponse, - summary="Get info about a running agent session", + summary="Get metrics for a running agent session", dependencies=[Depends(can_view_metrics)], ) async def get_session_metrics( session_id: str, session: Optional[AgentSession] = Depends(get_session), ) -> GetAgentSessionMetricsResponse: - """ - Get info about a running agent session. - """ + """Get metrics for a running agent session."""agents-core/vision_agents/core/agents/agent_launcher.py (1)
114-117: Critical bug: Incorrect use ofasyncio.as_completedand result inspection.Several issues compound here:
asyncio.as_completedreturns a regular iterator of awaitables, not anAsyncIterator- the cast is incorrect.- The
resultyielded byas_completedis an awaitable that must be awaited to get the actual result.cancel_and_waitreturnsNone, so calling.done(),.cancelled(),.exception()on the awaited result will fail.🐛 Proposed fix using `asyncio.gather`
coros = [cancel_and_wait(s.task) for s in self._sessions.values()] - async for result in cast(AsyncIterator[Task], asyncio.as_completed(coros)): - if result.done() and not result.cancelled() and result.exception(): - logger.error(f"Failed to cancel the agent task: {result.exception()}") + results = await asyncio.gather(*coros, return_exceptions=True) + for result in results: + if isinstance(result, Exception) and not isinstance(result, asyncio.CancelledError): + logger.error(f"Failed to cancel the agent task: {result}")You can also remove the unused
AsyncIteratorimport from line 10 after this fix.
🧹 Nitpick comments (6)
agents-core/vision_agents/core/runner/http/dependencies.py (1)
25-33: Consider guarding against missingapp.stateattributes.If
get_launcherorget_optionsis called before the FastAPI app is fully initialized (e.g., during testing or misconfiguration), accessingrequest.app.state.launcherorrequest.app.state.optionswill raiseAttributeError. A defensive check or clearer error message would improve debuggability.♻️ Optional defensive check
def get_launcher(request: Request) -> AgentLauncher: """ Get an agent launcher from the FastAPI app """ + if not hasattr(request.app.state, "launcher"): + raise RuntimeError("AgentLauncher not initialized in app.state") return request.app.state.launcheragents-core/vision_agents/core/runner/runner.py (1)
114-116: Preferlogging.getLevelName()overgetattr.Per coding guidelines, avoid
getattr. Uselogging.getLevelName()or a dictionary mapping for log level resolution.♻️ Suggested fix
- numeric_level = getattr(logging, log_level.upper(), logging.INFO) + numeric_level = logging.getLevelName(log_level.upper()) + if not isinstance(numeric_level, int): + numeric_level = logging.INFOBased on coding guidelines, avoid using
getattr.agents-core/vision_agents/core/runner/http/api.py (1)
209-219: Consider HTTP 503 for not-ready state.When the server isn't ready to accept requests, HTTP 503 (Service Unavailable) is more semantically appropriate than 400 (Bad Request). Load balancers and orchestrators typically interpret 503 as a transient condition.
♻️ Suggested change
if launcher.warmed_up and launcher.running: return Response(status_code=200) else: raise HTTPException( - status_code=400, detail="Server is not ready to accept requests" + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Server is not ready to accept requests" )agents-core/vision_agents/core/runner/http/options.py (1)
15-55: Addmodel_configto allow arbitrary types in Pydantic validation.
FastAPIinstances cannot be validated by Pydantic v2 withoutarbitrary_types_allowed=True. While the documented feature (passing a customfast_apiinstance) currently doesn't manifest in the codebase, it will fail if actually used.♻️ Suggested fix
+from pydantic import ConfigDict + class ServeOptions(pydantic_settings.BaseSettings): """ A collection of configuration options for the "serve" mode. ... """ + model_config = ConfigDict(arbitrary_types_allowed=True) + fast_api: Optional[FastAPI] = Noneagents-core/vision_agents/core/agents/agent_launcher.py (2)
46-50: Consider adding a return type annotation towait().The method awaits a task but lacks a return type hint.
✏️ Suggested fix
- async def wait(self): + async def wait(self) -> None: """ Wait for the session task to finish running. """ return await self.task
65-65: IncompleteCoroutinetype parameters onjoin_call.The
Coroutinegeneric requires three type parameters[T_co, T_contra, V_co]. Currently unspecified, which may cause type checker warnings.✏️ Suggested fix
- join_call: Callable[["Agent", str, str], Coroutine], + join_call: Callable[["Agent", str, str], Coroutine[Any, Any, None]],
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
agents-core/vision_agents/core/__init__.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runner/__init__.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/dependencies.pyagents-core/vision_agents/core/runner/http/options.pyagents-core/vision_agents/core/runner/runner.py
🚧 Files skipped from review as they are similar to previous changes (1)
- agents-core/vision_agents/core/runner/init.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/__init__.pyagents-core/vision_agents/core/runner/http/options.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/dependencies.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/agents/agent_launcher.py
🧠 Learnings (1)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*.py : Never write `except Exception as e` - use specific exception handling
Applied to files:
agents-core/vision_agents/core/runner/runner.py
🧬 Code graph analysis (5)
agents-core/vision_agents/core/__init__.py (6)
agents-core/vision_agents/core/agents/agent_launcher.py (2)
AgentLauncher(54-287)AgentSession(30-50)agents-core/vision_agents/core/runner/runner.py (2)
cli(234-348)Runner(43-348)agents-core/vision_agents/core/cli/cli_runner.py (1)
cli(25-152)dev.py (1)
cli(50-53)agents-core/vision_agents/core/edge/types.py (1)
User(15-18)agents-core/vision_agents/core/runner/http/options.py (1)
ServeOptions(15-55)
agents-core/vision_agents/core/runner/http/api.py (3)
agents-core/vision_agents/core/agents/agent_launcher.py (10)
AgentLauncher(54-287)AgentSession(30-50)SessionNotFoundError(26-26)get_session(209-211)start(99-106)stop(108-119)start_session(165-194)id(43-44)warmed_up(143-144)running(147-148)agents-core/vision_agents/core/runner/http/dependencies.py (6)
can_start_session(13-13)can_view_metrics(22-22)can_view_session(19-19)get_current_user(9-10)get_launcher(25-29)get_session(36-39)agents-core/vision_agents/core/runner/http/models.py (2)
GetAgentSessionMetricsResponse(40-53)GetAgentSessionResponse(31-37)
agents-core/vision_agents/core/runner/http/dependencies.py (1)
agents-core/vision_agents/core/agents/agent_launcher.py (3)
AgentLauncher(54-287)AgentSession(30-50)get_session(209-211)
agents-core/vision_agents/core/runner/runner.py (6)
agents-core/vision_agents/core/agents/agent_launcher.py (6)
AgentLauncher(54-287)start(99-106)launch(150-163)start_session(165-194)wait(46-50)stop(108-119)agents-core/vision_agents/core/runner/http/api.py (2)
lifespan(34-41)start_session(55-78)agents-core/vision_agents/core/runner/http/dependencies.py (5)
can_close_session(16-16)can_start_session(13-13)can_view_metrics(22-22)can_view_session(19-19)get_current_user(9-10)agents-core/vision_agents/core/runner/http/options.py (1)
ServeOptions(15-55)agents-core/vision_agents/core/edge/sfu_events.py (1)
level(496-500)plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py (1)
open_demo_for_agent(428-434)
agents-core/vision_agents/core/agents/agent_launcher.py (3)
agents-core/vision_agents/core/utils/utils.py (2)
cancel_and_wait(105-121)await_or_run(92-102)agents-core/vision_agents/core/runner/http/api.py (2)
start_session(55-78)close_session(86-102)agents-core/vision_agents/core/runner/http/dependencies.py (1)
get_session(36-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Mypy
- GitHub Check: unit / Test "not integration"
🔇 Additional comments (10)
agents-core/vision_agents/core/__init__.py (1)
1-15: LGTM!The public API surface is well-defined. Exports are cleanly organized, and
__all__correctly enumerates all symbols intended for public consumption.agents-core/vision_agents/core/runner/http/options.py (1)
7-12: LGTM!These no-op stubs serve as sensible defaults for permission callbacks, allowing all operations when no custom authorization is configured.
agents-core/vision_agents/core/runner/http/dependencies.py (1)
36-39: LGTM!The
get_sessiondependency cleanly composes withget_launcher, properly returningOptional[AgentSession]to allow callers to handle missing sessions.agents-core/vision_agents/core/runner/runner.py (1)
234-348: LGTM!The CLI is well-structured with clear option definitions and proper delegation to
run()andserve()methods.agents-core/vision_agents/core/runner/http/api.py (2)
33-41: LGTM!The lifespan context manager properly orchestrates launcher startup and shutdown, ensuring clean resource management.
130-156: LGTM!The
get_session_infoendpoint correctly handles the optional session dependency and returns a well-structured response.agents-core/vision_agents/core/agents/agent_launcher.py (4)
26-26: LGTM!The
SessionNotFoundErrorexception is cleanly defined for session lookup failures.
142-148: LGTM!The
warmed_upandrunningproperties provide clean read-only access to the launcher's state.
165-194: LGTM!The session lifecycle is well-managed: agent is launched, task is created with a cleanup callback, and session is properly tracked. The done callback correctly captures
agent.idby value using the default argument pattern.
209-211: LGTM!Simple and correct session lookup. The TODO comment for testing is noted.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/runner/http/api.py`:
- Around line 81-96: The DELETE endpoint always returns 204 because
AgentLauncher.close_session silently returns when a session does not exist;
update behavior so missing sessions produce 404: either modify
AgentLauncher.close_session to raise a clear exception (e.g., SessionNotFound)
when session_id isn't found, or change the router.close_session handler to check
the result of launcher.close_session (e.g., have it return a boolean) and raise
fastapi.HTTPException(status_code=404) when false/None; reference the
close_session endpoint function and the AgentLauncher.close_session method to
locate the changes and ensure non-existent sessions result in a 404 response.
In `@agents-core/vision_agents/core/runner/http/models.py`:
- Around line 39-40: Update the class docstring for
GetAgentSessionMetricsResponse to accurately reflect its purpose: replace
"Information about an active agent session." with "Metrics information for an
active agent session." so the documentation matches the model's intent.
In `@agents-core/vision_agents/core/runner/runner.py`:
- Around line 139-142: The conditional uses hasattr checks for agent.edge and
agent.edge.open_demo_for_agent; replace this with direct attribute access
guarded by a try/except or an explicit type/interface check: try to call or
reference agent.edge.open_demo_for_agent and catch AttributeError (or check
isinstance(agent.edge, ExpectedEdgeClass) or implement an Edge protocol) so the
code relies on the declared interface instead of hasattr. Update the block
around no_demo, agent, agent.edge and open_demo_for_agent to either perform a
try/except AttributeError when accessing agent.edge.open_demo_for_agent or
validate agent.edge against the concrete Edge class/protocol before calling
open_demo_for_agent.
In `@tests/test_agents/test_runner.py`:
- Line 3: Replace the unittest.mock MagicMock usage for stream_edge_mock with a
concrete fake or simple stub class: remove the import "from unittest.mock import
MagicMock", implement a lightweight FakeStreamEdge (or similar) inside the test
that implements the expected interface/attributes used by the tests, and
instantiate that fake in place of MagicMock wherever stream_edge_mock was used
(also replacing usages around the earlier occurrences noted at the other
occurrence spots). Ensure the fake provides the minimal behavior the tests rely
on (methods/attributes referenced in test_runner.py) so you can delete the
MagicMock import and any mock-specific assertions.
♻️ Duplicate comments (8)
agents-core/vision_agents/core/runner/http/api.py (2)
66-71: Avoid catching bareException.Per coding guidelines, never write
except Exception as e. Identify the specific exceptions thatlauncher.start_sessionmight raise and handle them explicitly. Based on learnings, this is a violation of project coding standards.
147-159: Copy-paste artifact: summary and docstring don't mention metrics.Both the
summary(line 150) and the docstring (lines 157-159) describe "Get info about a running agent session" rather than metrics. Update them to reflect the actual purpose of this endpoint.agents-core/vision_agents/core/agents/agent_launcher.py (2)
197-201: API contract mismatch:close_sessionsilently returns instead of raising an error.This was flagged in a prior review. The HTTP API may expect an exception for non-existent sessions to return a proper 404 response. Currently, all close requests return success regardless of session existence.
111-114:asyncio.as_completedusage issue persists.The loop iterates over awaitables from
asyncio.as_completedbut calls.done(),.cancelled(), and.exception()on them. Afteras_completedyields and you await,result.done()is alwaysTrue. Additionally, calling.exception()on a cancelled task raisesCancelledError.🐛 Proposed fix using asyncio.gather
coros = [cancel_and_wait(s.task) for s in self._sessions.values()] - async for result in cast(AsyncIterator[Task], asyncio.as_completed(coros)): - if result.done() and not result.cancelled() and result.exception(): - logger.error(f"Failed to cancel the agent task: {result.exception()}") + results = await asyncio.gather(*coros, return_exceptions=True) + for result in results: + if isinstance(result, Exception) and not isinstance(result, asyncio.CancelledError): + logger.error(f"Failed to cancel the agent task: {result}")agents-core/vision_agents/core/runner/runner.py (4)
155-157: Avoid catching bareException.Per coding guidelines, never write
except Exception as e. Identify specific exceptions that the agent session flow might raise and handle them explicitly. Based on learnings, this is a violation of project coding standards.
133-152:start_sessionalready creates an agent—avoid double-spawn.
launch()at line 134 creates an agent, thenstart_session()at line 149 launches a second one. The warmed/demo agent fromlaunch()is not the one in-session. Either remove the explicitlaunch()call and use the agent fromsession.agent, or modifystart_sessionto accept an existing agent.
68-75: Stale docstring:run_optionsparameter doesn't exist.The docstring mentions
run_options: instance of RunOptionsbut this parameter isn't present in the constructor signature.
209-212: Inconsistent: usesself._serve_optionsinstead ofoptionsparameter.
_create_fastapi_appaccepts anoptionsparameter but line 212 assignsself._serve_optionstoapp.state.options, bypassing the parameter.
🧹 Nitpick comments (7)
agents-core/vision_agents/core/runner/http/models.py (1)
30-36: Consider adding Field descriptions for consistency.
GetAgentSessionResponselacksField()descriptions whileGetAgentSessionMetricsResponseand other models use them. For API documentation consistency, consider adding descriptions.♻️ Optional enhancement
class GetAgentSessionResponse(BaseModel): """Information about an active agent session.""" - session_id: str - call_id: str - config: dict # TODO: Make it a type - session_started_at: datetime + session_id: str = Field(..., description="The ID of the agent session") + call_id: str = Field(..., description="The ID of the call") + config: dict = Field(..., description="Session configuration") # TODO: Make it a type + session_started_at: datetime = Field(..., description="Date and time in UTC at which the session was started")agents-core/vision_agents/core/runner/http/api.py (1)
197-207: Consider using 503 Service Unavailable instead of 400.A 400 Bad Request implies the client sent an invalid request. When the server isn't ready, 503 Service Unavailable is more semantically appropriate for readiness probes and aligns with Kubernetes conventions.
♻️ Suggested fix
`@router.get`("/ready") async def ready(launcher: AgentLauncher = Depends(get_launcher)) -> Response: """ Check if the server is ready to spawn new agents. """ if launcher.ready: return Response(status_code=200) else: raise HTTPException( - status_code=400, detail="Server is not ready to accept requests" + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Server is not ready to accept requests" )agents-core/vision_agents/core/agents/agent_launcher.py (2)
43-47: Missing return type annotation onwait()method.The
wait()method should have a return type annotation for clarity. Since it awaits the task, the return type depends on what the task returns.♻️ Suggested fix
- async def wait(self): + async def wait(self) -> None: """ Wait for the session task to finish running. """ return await self.task
61-62: IncompleteCoroutinetype hint.
Coroutinewithout type parameters is incomplete. For better type checking, specifyCoroutine[Any, Any, None](assuming the coroutine returns None).♻️ Suggested fix
- join_call: Callable[["Agent", str, str], Coroutine], + join_call: Callable[["Agent", str, str], Coroutine[Any, Any, None]],agents-core/vision_agents/core/runner/runner.py (1)
182-193: Incomplete docstring: Args lack descriptions.Per coding guidelines, docstrings should follow the Google style guide. The Args section lists parameters without descriptions.
📝 Suggested improvement
""" Start the HTTP server that spawns agents to the calls. Args: - host: - port: - agents_log_level: - http_log_level: - - Returns: - + host: Server hostname to bind to. + port: Server port to bind to. + agents_log_level: Logging level for agent-related logs. + http_log_level: Logging level for FastAPI and uvicorn. """tests/test_agents/test_runner.py (2)
83-100: Tests bypassLifespanManager, skipping lifespan startup/shutdown.Tests like
test_start_session_successcreate their ownAsyncClientwithoutLifespanManager. This meanslauncher.start()andlauncher.stop()from the lifespan won't be called, and the launcher won't be warmed up. Consider whether this is intentional for isolation or if it could mask lifespan-related bugs.♻️ Consider using LifespanManager for full integration
async def test_start_session_success(self, agent_launcher) -> None: runner = Runner(launcher=agent_launcher) - async with AsyncClient( - transport=ASGITransport(app=runner.fast_api), - base_url="http://test", - ) as client: + async with LifespanManager(runner.fast_api): + async with AsyncClient( + transport=ASGITransport(app=runner.fast_api), + base_url="http://test", + ) as client:
34-35: Method signature may not match the abstract base class.
on_warmed_upin the baseWarmableclass takesresource: Tas a parameter. Using*_catches it but obscures the expected signature. Consider matching the base signature for clarity.♻️ Match the Warmable signature
- async def on_warmed_up(self, *_) -> None: + def on_warmed_up(self, resource: bool) -> None: self.warmed_up = TrueNote: Also check if
on_warmed_upshould beasyncor sync based on the base class.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (6)
agents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/runner/runner.pypyproject.tomltests/test_agents/test_runner.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*test*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*test*.py: Never mock in tests; use pytest for testing
Mark integration tests with@pytest.mark.integrationdecorator
@pytest.mark.asynciois not needed - it is automatic
Files:
tests/test_agents/test_runner.py
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
tests/test_agents/test_runner.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/runner.pyagents-core/vision_agents/core/runner/http/models.py
🧠 Learnings (1)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*.py : Never write `except Exception as e` - use specific exception handling
Applied to files:
agents-core/vision_agents/core/runner/runner.py
🧬 Code graph analysis (3)
tests/test_agents/test_runner.py (4)
agents-core/vision_agents/core/agents/agent_launcher.py (3)
AgentLauncher(51-286)warmup(118-137)warmed_up(140-141)agents-core/vision_agents/core/runner/runner.py (1)
Runner(41-346)agents-core/vision_agents/core/llm/llm.py (2)
LLM(50-396)LLMResponseEvent(39-43)agents-core/vision_agents/core/warmup.py (1)
Warmable(47-107)
agents-core/vision_agents/core/agents/agent_launcher.py (3)
agents-core/vision_agents/core/agents/agents.py (1)
id(262-263)agents-core/vision_agents/core/utils/utils.py (2)
cancel_and_wait(105-121)await_or_run(92-102)agents-core/vision_agents/core/runner/http/dependencies.py (1)
get_session(36-39)
agents-core/vision_agents/core/runner/http/models.py (2)
agents-core/vision_agents/core/observability/agent.py (1)
description(12-13)agents-core/vision_agents/core/agents/agents.py (1)
metrics(1403-1404)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Mypy
- GitHub Check: unit / Ruff
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Mypy
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Ruff
🔇 Additional comments (5)
agents-core/vision_agents/core/agents/agent_launcher.py (1)
166-195: LGTM!The
start_sessionimplementation is well-structured. The done callback correctly capturesagent.idusing the default argument pattern to avoid late-binding closure issues.tests/test_agents/test_runner.py (1)
74-82: LGTM!The test class structure and the health/ready endpoint tests are well-organized and appropriately use the shared fixtures.
pyproject.toml (3)
77-87: LGTM!These exclusion patterns appropriately filter out build artifacts, caches, and test directories from the workspace. The globs are standard fare for Python projects.
121-123: LGTM!A mere whisper of whitespace—formatting tidied, nothing altered in substance.
109-109: Sensible addition for testing the new HTTP server.
asgi-lifespan>=2.1.0is well-suited for testing FastAPI lifespan events, which aligns with the agent HTTP server introduced in this PR.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/runner/runner.py`:
- Around line 109-110: The use of getattr to resolve the logging level should be
replaced by an explicit mapping: create a LOG_LEVELS dict mapping strings like
"DEBUG","INFO","WARNING","ERROR","CRITICAL" to logging.DEBUG,... then compute
numeric_level = LOG_LEVELS.get(log_level.upper(), logging.INFO) (or validate and
log/warn if log_level is invalid) and pass numeric_level into
configure_sdk_logger; update the references around the numeric_level variable
and the call to configure_sdk_logger to use this validated mapping instead of
getattr.
In `@tests/test_agents/test_runner.py`:
- Around line 80-81: Add the "integration" pytest marker to the HTTP lifecycle
test by decorating the TestRunnerServe class or its test_health method with
`@pytest.mark.integration` and ensure pytest is imported; specifically update the
TestRunnerServe class or async def test_health(...) in
tests/test_agents/test_runner.py to include the `@pytest.mark.integration`
decorator so the FastAPI + lifespan + HTTP test is classified as an integration
test.
♻️ Duplicate comments (6)
agents-core/vision_agents/core/runner/runner.py (5)
64-71: Remove non-existentrun_optionsfromRunner.__init__docstring.The parameter isn’t in the signature, so this is misleading.
📝 Suggested fix
Args: launcher: instance of `AgentLauncher` serve_options: instance of `ServeOptions` to configure behavior in `serve` mode. - run_options: instance of `RunOptions` to configure behavior in `run` mode.
125-131:start_sessionalready spawns an agent—avoid double-spawn.You’re launching an agent and then starting a session that likely creates another one. Use the session’s agent instead.
🐛 Suggested fix
# Start the agent launcher. await self._launcher.start() - - # Create the agent - agent = await self._launcher.launch() - - logger.info("✅ Agent warmed up and ready") ... - # Join call if join_call function is provided logger.info(f"📞 Joining call: {call_type}/{call_id}") session = await self._launcher.start_session( call_id, call_type, video_track_override_path=video_track_override ) + agent = session.agent + + logger.info("✅ Agent warmed up and ready") await session.wait()Also applies to: 143-147
134-141: Avoidhasattrchecks; use direct access guarded byAttributeError.This avoids the banned
hasattrusage while keeping behavior safe.♻️ Suggested fix
- if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) + if not no_demo: + try: + open_demo = agent.edge.open_demo_for_agent + except AttributeError: + pass + else: + logger.info("🌐 Opening demo UI...") + await open_demo(agent, call_type, call_id)As per coding guidelines.
149-152: Replace broadexcept Exceptionwith specific exceptions.Catching everything hides unexpected failures and violates the project rule.
Based on learnings.
205-208: Use theoptionsparameter when wiringapp.state.options.The current assignment ignores the provided parameter, which can break custom options.
🐛 Suggested fix
def _create_fastapi_app(self, options: ServeOptions) -> FastAPI: app = FastAPI(lifespan=lifespan) app.state.launcher = self._launcher - app.state.options = self._serve_options + app.state.options = optionstests/test_agents/test_runner.py (1)
4-4: ReplaceMagicMockwith a lightweight fake edge.Mocking is disallowed in tests per guidelines; use a small fake instead.
♻️ Suggested fix
-from unittest.mock import MagicMock ... +class FakeStreamEdge: + def __init__(self): + self.events = EventManager() ... async def create_agent(**kwargs) -> Agent: - stream_edge_mock = MagicMock() - stream_edge_mock.events = EventManager() + stream_edge = FakeStreamEdge() ... return Agent( llm=DummyLLM(), tts=DummyTTS(), - edge=stream_edge_mock, + edge=stream_edge, agent_user=User(name="test"), )As per coding guidelines.
Also applies to: 43-49
🧹 Nitpick comments (1)
tests/test_agents/test_runner.py (1)
53-55: Avoid long sleep injoin_callstub.If
join_callis awaited during session startup, this adds 10s per test. Consider a near-zero sleep (or immediate return) unless the delay is essential.♻️ Suggested fix
async def join_call(*args, **kwargs): - await asyncio.sleep(10) + await asyncio.sleep(0)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
agents-core/vision_agents/core/runner/runner.pytests/test_agents/test_runner.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*test*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*test*.py: Never mock in tests; use pytest for testing
Mark integration tests with@pytest.mark.integrationdecorator
@pytest.mark.asynciois not needed - it is automatic
Files:
tests/test_agents/test_runner.py
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
tests/test_agents/test_runner.pyagents-core/vision_agents/core/runner/runner.py
🧠 Learnings (2)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*test*.py : Never mock in tests; use pytest for testing
Applied to files:
tests/test_agents/test_runner.py
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*.py : Never write `except Exception as e` - use specific exception handling
Applied to files:
agents-core/vision_agents/core/runner/runner.py
🧬 Code graph analysis (1)
agents-core/vision_agents/core/runner/runner.py (3)
agents-core/vision_agents/core/runner/http/api.py (2)
lifespan(34-41)start_session(55-78)agents-core/vision_agents/core/runner/http/dependencies.py (5)
can_close_session(16-16)can_start_session(13-13)can_view_metrics(22-22)can_view_session(19-19)get_current_user(9-10)agents-core/vision_agents/core/runner/http/options.py (1)
ServeOptions(15-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Ruff
- GitHub Check: unit / Mypy
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Mypy
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@agents-core/vision_agents/core/runner/runner.py`:
- Around line 46-55: The doc example incorrectly calls launcher.cli() implying
AgentLauncher exposes cli(), but the CLI entrypoint is provided by Runner.cli();
update the example to construct and call Runner(...).cli() (or show that Runner
is created from AgentLauncher if that's intended) so it references Runner and
its cli() method (e.g., use Runner(...) and call .cli() instead of
AgentLauncher(...).cli()), ensuring the example matches the Runner.cli()
implementation.
♻️ Duplicate comments (8)
agents-core/vision_agents/core/runner/runner.py (5)
108-109: Avoidgetattrfor log level mapping; use an explicit map.
getattr(logging, ...)violates the no-getattrguideline and allows unexpected attributes. Prefer a fixed mapping shared byrun()andserve(). As per coding guidelines.♻️ Proposed refactor
+LOG_LEVELS = { + "CRITICAL": logging.CRITICAL, + "ERROR": logging.ERROR, + "WARNING": logging.WARNING, + "INFO": logging.INFO, + "DEBUG": logging.DEBUG, +} ... - numeric_level = getattr(logging, log_level.upper(), logging.INFO) + numeric_level = LOG_LEVELS.get(log_level.upper(), logging.INFO) ... - configure_sdk_logger( - level=getattr(logging, agents_log_level.upper(), logging.INFO) - ) + configure_sdk_logger( + level=LOG_LEVELS.get(agents_log_level.upper(), logging.INFO) + ) - configure_fastapi_loggers( - level=getattr(logging, http_log_level.upper(), logging.INFO) - ) + configure_fastapi_loggers( + level=LOG_LEVELS.get(http_log_level.upper(), logging.INFO) + )Also applies to: 190-195
124-147:start_sessionalready launches an agent — avoid double-spawn.You launch an agent and then call
start_session()(which launches again), so the warmed agent isn’t the one in-session. This can leak resources and confuse demo UI behavior.🐛 Suggested fix
- # Create the agent - agent = await self._launcher.launch() - - logger.info("✅ Agent warmed up and ready") - - # Open demo UI by default - if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) - - # Join call if join_call function is provided logger.info(f"📞 Joining call: {call_type}/{call_id}") session = await self._launcher.start_session( call_id, call_type, video_track_override_path=video_track_override ) + agent = session.agent + + logger.info("✅ Agent warmed up and ready") + + # Open demo UI by default + if ( + not no_demo + and hasattr(agent, "edge") + and hasattr(agent.edge, "open_demo_for_agent") + ): + logger.info("🌐 Opening demo UI...") + await agent.edge.open_demo_for_agent(agent, call_type, call_id) + await session.wait()
133-140: Avoidhasattr; use direct access withAttributeErrorguard.Guidelines disallow
hasattr. Prefer direct access with a narrowtry/except AttributeError(or a typed interface). As per coding guidelines.
148-152: Replaceexcept Exception as ewith specific exceptions.Catching
Exceptionviolates the guideline and can swallow unexpected failures. Handle known exceptions explicitly and let others bubble. As per coding guidelines.
204-207: Use theoptionsparameter when settingapp.state.options.
_create_fastapi_appreceivesoptionsbut assignsself._serve_options, which ignores the passed value and can surprise callers.🐛 Suggested fix
- app.state.options = self._serve_options + app.state.options = optionsagents-core/vision_agents/core/runner/http/api.py (2)
62-71: Avoidexcept Exception as e; handle specific launcher errors.The broad catch violates the guideline and can mask programming errors. Use specific exceptions from
AgentLauncherand re-raise unexpected ones. As per coding guidelines.
158-170: Metrics endpoint summary/docstring should mention metrics.The summary and docstring still say “info about a running agent session,” which is misleading for
/metrics.✏️ Suggested doc fix
`@router.get`( "/sessions/{session_id}/metrics", response_model=GetAgentSessionMetricsResponse, - summary="Get info about a running agent session", + summary="Get metrics for a running agent session", ) ... - """ - Get info about a running agent session. - """ + """Get metrics for a running agent session."""agents-core/vision_agents/core/agents/agent_launcher.py (1)
111-114:asyncio.as_completedyields awaitables; current stop() loop is incorrect.
resultis an awaitable/task wrapper, not the awaited outcome. Calling.done()/.exception()without awaiting is invalid and can mask failures.🐛 Suggested fix
- coros = [cancel_and_wait(s.task) for s in self._sessions.values()] - async for result in cast(AsyncIterator[Task], asyncio.as_completed(coros)): - if result.done() and not result.cancelled() and result.exception(): - logger.error(f"Failed to cancel the agent task: {result.exception()}") + coros = [cancel_and_wait(s.task) for s in self._sessions.values()] + results = await asyncio.gather(*coros, return_exceptions=True) + for result in results: + if isinstance(result, Exception) and not isinstance(result, asyncio.CancelledError): + logger.error(f"Failed to cancel the agent task: {result}")
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
agents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/runner/runner.pytests/test_agents/test_runner.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_agents/test_runner.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (.cursor/rules/python.mdc)
**/*.py: Never adjust sys.path in Python code
Never writeexcept Exception as e- use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings
Files:
agents-core/vision_agents/core/runner/http/api.pyagents-core/vision_agents/core/runner/http/models.pyagents-core/vision_agents/core/agents/agent_launcher.pyagents-core/vision_agents/core/runner/runner.py
🧠 Learnings (2)
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*.py : Never write `except Exception as e` - use specific exception handling
Applied to files:
agents-core/vision_agents/core/runner/runner.py
📚 Learning: 2025-11-24T17:04:43.030Z
Learnt from: CR
Repo: GetStream/Vision-Agents PR: 0
File: .cursor/rules/python.mdc:0-0
Timestamp: 2025-11-24T17:04:43.030Z
Learning: Applies to **/*.py : Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Applied to files:
agents-core/vision_agents/core/runner/runner.py
🧬 Code graph analysis (1)
agents-core/vision_agents/core/runner/runner.py (5)
agents-core/vision_agents/core/agents/agent_launcher.py (6)
AgentLauncher(51-299)start(96-103)launch(151-164)start_session(166-195)wait(43-47)stop(105-116)agents-core/vision_agents/core/utils/logging.py (2)
configure_fastapi_loggers(59-95)configure_sdk_logger(36-56)agents-core/vision_agents/core/runner/http/api.py (2)
lifespan(34-41)start_session(55-78)agents-core/vision_agents/core/runner/http/dependencies.py (5)
can_close_session(16-16)can_start_session(13-13)can_view_metrics(22-22)can_view_session(19-19)get_current_user(9-10)agents-core/vision_agents/core/runner/http/options.py (1)
ServeOptions(15-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Mypy
- GitHub Check: unit / Test "not integration"
- GitHub Check: unit / Validate extra dependencies in "agents-core/pyproject.toml"
- GitHub Check: unit / Ruff
- GitHub Check: unit / Mypy
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
- new URL scheme with agents sessions - merge CLI and HTTP runner in a single Runner class
00bd7c6 to
a51d7e5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@examples/08_agent_server_example/README.md`:
- Around line 20-25: Fix markdownlint issues in README.md: indent nested list
items under the requirements by two spaces, change the plaintext .env code fence
to include the language tag "dotenv", convert the bold pseudo-headings
"**Example: JWT Authentication with Database Lookup**" and "**Granular
Permissions**" into real Markdown headings (e.g., "#### Example: JWT
Authentication with Database Lookup" and "#### Granular Permissions"), and turn
the raw Swagger UI URL "http://127.0.0.1:8000/docs" into a markdown link or
autolink (e.g., [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)); apply
the same fixes to the other noted occurrences (lines referenced) so all
instances are lint-clean.
♻️ Duplicate comments (5)
agents-core/vision_agents/core/runner/runner.py (5)
109-111: Replacegetattrlog-level lookups with an explicit mapping.This violates the project’s Python guidelines and is already noted in prior review. Prefer a dict-based mapping for clarity and validation. As per coding guidelines, avoid
getattrin Python.♻️ Proposed fix
+LOG_LEVELS = { + "CRITICAL": logging.CRITICAL, + "ERROR": logging.ERROR, + "WARNING": logging.WARNING, + "INFO": logging.INFO, + "DEBUG": logging.DEBUG, +} ... - numeric_level = getattr(logging, log_level.upper(), logging.INFO) + numeric_level = LOG_LEVELS.get(log_level.upper(), logging.INFO) ... - configure_sdk_logger( - level=getattr(logging, agents_log_level.upper(), logging.INFO) - ) + configure_sdk_logger( + level=LOG_LEVELS.get(agents_log_level.upper(), logging.INFO) + ) - configure_fastapi_loggers( - level=getattr(logging, http_log_level.upper(), logging.INFO) - ) + configure_fastapi_loggers( + level=LOG_LEVELS.get(http_log_level.upper(), logging.INFO) + )Run the following to confirm all
getattr(logging, ...)uses are removed after the refactor:#!/bin/bash rg -n "getattr\\(logging" agents-core/vision_agents/core/runner/runner.pyAlso applies to: 192-197
130-148: Avoid double-spawn:start_session()already launches an agent.The flow launches an agent and then starts a session that creates another agent, so the warmed agent is discarded. Use the session’s agent (or pass the warmed agent into
start_session). This mirrors prior review feedback.🐛 Suggested fix
- # Create the agent - agent = await self._launcher.launch() - - logger.info("✅ Agent warmed up and ready") - - # Open demo UI by default - if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) - # Join call if join_call function is provided logger.info(f"📞 Joining call: {call_type}/{call_id}") session = await self._launcher.start_session( call_id, call_type, video_track_override_path=video_track_override ) + agent = session.agent + + logger.info("✅ Agent warmed up and ready") + + # Open demo UI by default + if ( + not no_demo + and hasattr(agent, "edge") + and hasattr(agent.edge, "open_demo_for_agent") + ): + logger.info("🌐 Opening demo UI...") + await agent.edge.open_demo_for_agent(agent, call_type, call_id) + await session.wait()
136-140: Avoidhasattrchecks; use explicit access or a protocol.Project guidelines discourage
hasattr. Use a guarded attribute access or an interface check instead. As per coding guidelines, avoidhasattrin Python.♻️ Suggested alternative
- if ( - not no_demo - and hasattr(agent, "edge") - and hasattr(agent.edge, "open_demo_for_agent") - ): - logger.info("🌐 Opening demo UI...") - await agent.edge.open_demo_for_agent(agent, call_type, call_id) + if not no_demo: + try: + open_demo = agent.edge.open_demo_for_agent + except AttributeError: + pass + else: + logger.info("🌐 Opening demo UI...") + await open_demo(agent, call_type, call_id)Verify there are no remaining
hasattrusages in this module:#!/bin/bash rg -n "hasattr\\(" agents-core/vision_agents/core/runner/runner.py
152-153: Avoidexcept Exception as e; handle specific errors.The project rules prohibit a blanket
Exceptioncatch. Please enumerate the explicit exception types thatstart,launch, orstart_sessioncan raise and handle them, letting unknown errors propagate. As per coding guidelines, avoidexcept Exception as e.Identify the concrete exceptions to catch by inspecting the launcher/session code:
#!/bin/bash rg -n "raise " agents-core/vision_agents/core/agents/agent_launcher.py rg -n "Exception" agents-core/vision_agents/core/agents/agent_launcher.py
206-210: Use theoptionsparameter when settingapp.state.options.
_create_fastapi_app()takesoptionsbut storesself._serve_optionsinstead, which ignores the caller’s argument. This was noted earlier and should be aligned with the method signature.🐛 Suggested fix
- app.state.options = self._serve_options + app.state.options = options
What's changed
Runnerthat supports both console and HTTP server modes for the same agents (see06_agent_server_examplefor details)Summary by CodeRabbit
New Features
Dependencies
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.