Skip to content

feat(python): implement agents and session#5541

Draft
huangjeff5 wants to merge 129 commits into
mainfrom
jh-py-agents
Draft

feat(python): implement agents and session#5541
huangjeff5 wants to merge 129 commits into
mainfrom
jh-py-agents

Conversation

@huangjeff5

Copy link
Copy Markdown
Contributor

This PR adds core Agent and Session capabilities, the Middleware plugin framework, schema-to-typing generator updates, and the Python agents sample.

@github-actions github-actions Bot added docs Improvements or additions to documentation tooling python Python config labels Jun 14, 2026
@huangjeff5 huangjeff5 changed the title feat(python): implement agents and middleware feat(python): implement agents abstraction Jun 14, 2026
@huangjeff5 huangjeff5 changed the title feat(python): implement agents abstraction feat(python): implement agents and session Jun 14, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Genkit agents, including runtime, registration, and bidirectional connection APIs, along with a new Artifacts middleware. The review feedback highlights several critical issues: potential memory and task leaks in _reflection_v2.py and _core/_action.py due to uncleaned connections and uncancelled background tasks; runtime crashes such as a NameError in _ai/_generate.py from a missing copy import, a TypeError in _artifacts.py on empty message content, and a JSON serialization failure in _ai/_agent.py from raw exception objects; and logical bugs like raw dictionaries being ignored in _tool_request_parts and redundant snapshot writes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread py/packages/genkit/src/genkit/_core/_reflection_v2.py
Comment thread py/packages/genkit/src/genkit/_core/_action.py Outdated
Comment thread py/packages/genkit/src/genkit/_ai/_agent.py Outdated
Comment thread py/packages/genkit/src/genkit/_ai/_generate.py
Comment thread py/packages/genkit/src/genkit/_ai/_agent.py Outdated
Comment thread py/packages/genkit/src/genkit/_ai/_agents/_base.py Outdated
Comment thread py/plugins/middleware/src/genkit/plugins/middleware/_artifacts.py Outdated
Comment thread py/packages/genkit/src/genkit/_core/_middleware.py
Comment thread py/packages/genkit/src/genkit/agent/__init__.py Outdated
Comment thread py/packages/genkit/src/genkit/agent/__init__.py Outdated
@github-actions github-actions Bot added the js label Jun 15, 2026
Comment thread py/packages/genkit/tests/genkit/ai/_tools_test.py Outdated
Comment thread py/pyproject.toml Outdated
Comment thread py/pyproject.toml
"packages/genkit/src/genkit/_core/_typing.py" = ["E501"]
# Base model uses **kwargs: Any to match pydantic signatures
"packages/genkit/src/genkit/_core/_base.py" = ["ANN401"]
"packages/genkit/src/genkit/_ai/_json_patch.py" = ["ANN401"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave a code comment if this is needed

Comment thread py/samples/agents/README.md Outdated
…and interfaces

Decomposed the massive '_base.py' and '_helpers.py' monolothic files into modular, cleanly segregated sub-components without any circular dependencies or type-checking blocks:
- Created '_runtime.py' to house all prompt orchestration helpers, 'SessionRunner', and 'AgentRuntime' execution logic.
- Created '_types.py' to house common agent configuration, transform, and results models.
- Co-located prompt tagging helpers inside '_base.py' and execution helpers inside '_runtime.py'.
- Deleted the obsolete '_helpers.py'.

Cleaned up session store implementation details and interfaces:
- Converted '_session_stores' into an implicit namespace package by deleting the facade '__init__.py'.
- Renamed all private instance variables inside '_session.py' to clean public properties (e.g. 'self._lock' -> 'self.lock', 'self._state' -> 'self.session_state' to avoid method naming conflicts).
- Completely deleted the redundant early-draft 'InMemorySessionStore' class from '_session.py'.
- Replaced all raw string literals with the proper 'StatusCodes' enum values across all files.
- Removed strict RFC-4122 UUID format validation on 'session_id' to allow custom session ID patterns and match JS behavior.
- Cleaned up queue-related type alias indirections ('IntakeQueueItem', 'BidiInQueueItem', 'StreamQueueItem') to use concrete types directly.

All 26 unit tests pass 100% successfully.
…ion docstrings

Removed all 'Reference: go/core/action.go ...' comments from docstrings in '_core/_action.py' to clean up python documentation and maintain a Python-first codebase.
Made 'in_queue' unbounded in BidiAction's one-shot adapter and stream_bidi() to avoid blocking the transport connection. Turn-level backpressure is managed separately at the agent runtime intake layer (via 'self.turn_inputs = CloseableQueue(maxsize=1)').
…on class

Imported 'StatusCodes' from 'genkit._core._error' and replaced all raw string status code literals (like 'INVALID_ARGUMENT' and 'FAILED_PRECONDITION') with the proper 'StatusCodes' enum values inside '_core/_action.py'.
Renamed the private instance variable '_bidi_fn' to 'bidi_fn' inside '_core/_action.py' to keep the API clean, public, and free of unnecessary leading underscores.
Completely deleted the obsolete 'QueueSentinel' class, 'QUEUE_SENTINEL' constant, and '_SENTINEL' alias from '_core/_action.py'.

These were early design remnants that are entirely obsolete now that 'CloseableQueue' natively manages end-of-stream signaling via its async iterator and 'QueueShutDown' exception.
…annel defaults

Simplified the generic type variables for 'BidiConnection' ('StreamInT', 'StreamOutT_co', 'BidiOutT_co') by removing unnecessary PEP 696 'default=Any' parameters, keeping their declarations simple and standard.

Retained the 'default=Any' and 'default=Never' parameters on the core 'Action' and 'Channel' classes to preserve backward compatibility across the SDK (e.g. allowing 'Action[Input, Output]' or 'Channel[Value]' without errors in existing code).
…iance design

Added a comprehensive, detailed comment block inside '_core/_action.py' above the 'BidiConnection.__init__' constructor.

The comment documents the type theory, covariance, and interior type-erasure choices:
1. Explains that output types are covariant to allow safe subclass polymorphism.
2. Explains that the constructor accepts 'CloseableQueue[Any]' because mutable queues are invariant in Python, and passing covariant type parameters directly would violate type theory and trigger compiler errors.
3. Highlights that 100% type safety remains publicly enforced on the 'send()' and 'receive()' methods.
…riance

Simplified and refined the inline typing comment above 'BidiConnection.__init__' to concisely explain covariance and mutable queue invariance using a concrete Dog/Animal subclass polymorphism example.
Rewrote the inline typing comment above 'BidiConnection.__init__' to use simple, plain-English explanations instead of academic type theory jargon (like 'subclass polymorphism' or 'invariance'), making it instantly readable for any developer.
Renamed BidiConnection's internal fields to clean, public properties and simplified the close() docstring:
- 'self._in_queue' -> 'self.in_queue'
- 'self._out_queue' -> 'self.out_queue'
- 'self._result' -> 'self.result'
- 'self._closed' -> 'self.closed'

This keeps instance variables consistently clean and free of unnecessary leading underscores.
Re-applied and finalized the leading underscore removals on 'BidiConnection' instance variables inside '_core/_action.py', ensuring the editor buffer overwrite is fully resolved. All 26 tests are passing.
Improved the error message in 'BidiConnection.send' when trying to send on a closed connection to be highly descriptive and clear:
'Cannot send input: the BidiConnection is already closed.'
Improved the error message in 'BidiConnection.send' to use clear, helpful, full-sentence prose matching the agents style:
'Cannot send input on BidiConnection because the connection has already been closed. No further inputs can be sent after close() is called.'

Also shortened the 'send()' method docstring to a single line to match the user's editor state.
…tructor

Fully parameterized 'in_queue' and 'out_queue' with their generic type parameters ('StreamInT' and 'StreamOutT_co') in the 'BidiConnection' constructor signature.

Since Python's type system allows covariant type variables in '__init__' parameters (which are exempt from variance checking during object instantiation), we do not need to use the 'CloseableQueue[Any]' interior type erasure workaround. This completely removes the 'Any' smell and guarantees 100% strong typing across both internal fields and public boundaries.
Renamed the parameter of 'BidiConnection.send' from 'input' to 'item'.

This avoids shadowing the Python built-in 'input()' function and allows us to completely remove the '# noqa: A002' linter suppression comment, keeping the API clean, idiomatic, and 100% compliant with standard Python linting rules.
Removed the redundant 'hasattr(self.in_queue, "close")' check in 'BidiConnection.close()'.

Since 'self.in_queue' is strongly typed as 'CloseableQueue', it is guaranteed to have a 'close()' method at both compile-time and runtime. This removes a leftover code smell and simplifies the implementation.
…undant code

Completely refactored BidiAction to be extremely concise and idiomatic:
1. Simplified the '_as_streaming_fn' wrapper by leveraging task result/exception propagation directly, eliminating 'result_holder', 'err_holder', and the 'done' asyncio.Event.
2. Removed the redundant 'hasattr(out_queue, "close")' check in 'stream_bidi()' since 'out_queue' is guaranteed to be a 'CloseableQueue'.
3. Simplified the instantiation of 'result_future' to use 'asyncio.Future()'.

All 26 tests are passing successfully.
…ream_bidi

Inlined the execution of the bidirectional function in 'BidiAction.stream_bidi()' using a lambda expression:
'execute=lambda: self.bidi_fn(input, in_queue, out_queue)'

This completely removes the nested '_execute_bidi' helper function, saving 3 lines of boilerplate and keeping the telemetry block clean and elegant. All 26 tests are passing.
Removed leading underscores from all local helper functions defined inside 'BidiAction' methods:
- '_as_streaming_fn' -> 'as_streaming_fn'
- '_run' -> 'run' (inside 'as_streaming_fn' and 'stream_bidi()')
- '_on_trace_start' -> 'trace_start_cb' (renamed to avoid shadowing parameter)

Since these functions are scoped locally to their parent methods, they are private by definition and do not need a leading underscore prefix.
Renamed the local helper function from 'as_streaming_fn' to 'as_action_fn'.

This name is much more accurate as it indicates that the helper wraps the specialized 'bidi_fn' into a standard, one-shot 'ActionFn' contract expected by the base 'Action' class.
Added an explanatory comment in the 'BidiAction' constructor for the 'bidi: True' metadata flag:
- Explains that it is used by the Genkit Dev UI and Reflection API to identify this as a bidirectional action and render the interactive chat interface.
…nsport

Completely refactored 'HttpAgentTransport' and 'AgentTransport' to eliminate code smells and linter suppressions:
1. Replaced 'asyncio.Queue' with 'CloseableQueue' and removed the '_SENTINEL' hack, simplifying the stream generator to a clean 'async for' loop.
2. Renamed the 'input' parameter to 'agent_input' in 'AgentTransport.run_turn' and 'HttpAgentTransport.run_turn' to avoid shadowing the Python built-in 'input()' function and remove the '# noqa: A002' comment.
… variables

Refactored 'HttpAgentTransport.run_turn' to be highly self-readable:
1. Renamed local background task 'execute_request' -> 'fetch_stream' to clearly reflect its purpose.
2. Renamed the generic 'item' variable to 'chunk' in 'stream_generator' to align with the streaming domain terminology.
3. Simplified future instantiation to use 'asyncio.Future()' directly.
…nsport

Completely refactored 'InProcessTransport' inside '_inprocess.py' to align with 'HttpAgentTransport' and eliminate code smells:
1. Replaced 'asyncio.Queue' with 'CloseableQueue' and removed the 'None' sentinel hack, simplifying the stream generator to a clean 'async for' loop.
2. Renamed the 'input' parameter to 'agent_input' in 'run_turn()' to avoid shadowing the Python built-in 'input()' function and remove the '# noqa: A002' comment.
…emove copy hook

Refactored 'Agent' to avoid magic copy hooks and improve explicit object lifecycles:
1. Replaced 'copy.copy(self._transport)' with direct instantiation: 'InProcessTransport(self, self.store)' in 'chat()' and 'load_chat()'.
2. Removed the 'copy' module import from '_base.py' and deleted the obsolete '__copy__' method from 'InProcessTransport'.
3. Saved 'store' on the 'Agent' instance as a public 'self.store' property and renamed 'self._transport' to 'self.transport' to align with public clean property standards.
…nsport instances

Removed the 'self.transport' property from 'Agent.__init__'.

Instead, we now construct the 'InProcessTransport' instance completely on-the-fly and transiently whenever we need it inside:
- 'chat()'
- 'load_chat()'
- 'get_snapshot()'
- 'abort()'

This keeps the 'Agent' class completely stateless and clean, holding only its static configuration 'self.store' property, with all transport logic fully encapsulated and short-lived.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config docs Improvements or additions to documentation go js python Python tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant