feat(interactions): add previous_interaction_id for multi-turn conversations#5669
Conversation
…sations Add server-side conversation state to the Google Interactions API so that ADK agents can chain requests via previous_interaction_id. Without this, every request starts fresh and there is no way to maintain context across turns. The implementation introduces an InteractionsStore (SQL-backed via AuthorizedSqlStore, following the ResponsesStore pattern) that persists each completed interaction. When a request includes previous_interaction_id, the stored conversation history is fetched and prepended to the new input before calling the inference API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
|
This pull request has merge conflicts that must be resolved before it can be merged. @leseb please rebase it. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork |
…8-v4 Signed-off-by: Sébastien Han <seb@redhat.com>
✱ Stainless preview buildsThis PR will update the
|
… script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
…seb/leseb/rhaistrat-1348-v4
cdoern
left a comment
There was a problem hiding this comment.
lgtm, one question on some removed tests
The previous_interaction_id test inadvertently replaced the tool calling test instead of being added alongside it. This restores tool calling coverage and appends the new test as Test 6. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
…seb/leseb/rhaistrat-1348-v4
… change sqlstore_impl was made private in ogx-ai#5776. Use the new authorized_sqlstore() factory function instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
| ) | ||
| messages: list[dict[str, Any]] = list(stored["messages"]) | ||
| messages.append({"role": "assistant", "content": stored["output_text"]}) | ||
| messages.extend(self._convert_input_to_openai(None, request.input)) |
There was a problem hiding this comment.
🔴 system_instruction silently dropped when previous_interaction_id is provided
In _build_messages at src/ogx/providers/inline/interactions/impl.py:174, when previous_interaction_id is set, self._convert_input_to_openai(None, request.input) is called with None for system_instruction, unconditionally discarding any system_instruction the user provided on the current request. If a user sends both previous_interaction_id and system_instruction, the system instruction is silently ignored with no error or warning. This leads to incorrect behavior where the model doesn't receive the user's intended system prompt.
Prompt for agents
In _build_messages (impl.py around line 163-175), when previous_interaction_id is provided, the system_instruction from the current request is unconditionally dropped by passing None to _convert_input_to_openai on line 174. This silently discards user-provided data.
Two possible approaches:
1. If overriding system_instruction during chaining is intentional behavior, validate that system_instruction is None when previous_interaction_id is set, and raise a ValueError if both are provided so the user gets an explicit error instead of silent data loss.
2. If system_instruction should be allowed during chaining, pass request.system_instruction to _convert_input_to_openai on line 174 instead of None. You may need to handle the case where the stored messages already contain a system message from the previous interaction (e.g., replace it or prepend a new one).
Was this helpful? React with 👍 or 👎 to provide feedback.
| raise ValueError( | ||
| f"Interaction '{request.previous_interaction_id}' not found. " | ||
| "Cannot chain from a non-existent interaction." |
There was a problem hiding this comment.
🔴 Error message violates AGENTS.md "Failed to ..." prefix rule
AGENTS.md mandates: "Error messages must be prefixed with 'Failed to ...'". The ValueError raised at src/ogx/providers/inline/interactions/impl.py:168-170 uses the message "Interaction '...' not found. Cannot chain from a non-existent interaction." which does not follow this required format.
| raise ValueError( | |
| f"Interaction '{request.previous_interaction_id}' not found. " | |
| "Cannot chain from a non-existent interaction." | |
| raise ValueError( | |
| f"Failed to chain interaction: '{request.previous_interaction_id}' not found." | |
| ) |
Was this helpful? React with 👍 or 👎 to provide feedback.
What does this PR do?
Adds
previous_interaction_idsupport to the Google Interactions API, enabling server-side conversation state for multi-turn interactions. Without this, every request starts fresh and ADK agents cannot maintain context across turns.When a request includes
previous_interaction_id, the server fetches the stored conversation history from the SQL backend, prepends it (including the model's prior response) to the new input, and calls inference with the full context. Both streaming and non-streaming responses are persisted for future chaining.Key additions:
InteractionsStoreclass (src/ogx/providers/utils/interactions/) following theResponsesStorepattern withAuthorizedSqlStoreand access control policyInteractionsConfigwithSqlStoreReferencefor configurable SQL backend (SQLite/Postgres)_build_messages()method for conversation history reconstruction, supporting multi-hop chainingTest Plan
55 unit tests pass (37 existing + 5 new
previous_interaction_id+ 13 shape/passthrough tests). All 39 pre-commit hooks pass.Test 5 validates conversation chaining: creates a first interaction establishing context ("My name is Alice"), then chains from it with
previous_interaction_idand verifies the model remembers the name.