mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-15 09:12:18 +00:00
Merge fda103e8b5 into ce73cf8b9d
This commit is contained in:
commit
55969c287c
5 changed files with 68 additions and 9 deletions
|
|
@ -103,6 +103,7 @@ async def acreate(
|
|||
custom_llm_provider: str | None = None,
|
||||
*,
|
||||
agent_config: Mapping[str, object] | None = None,
|
||||
continuation_token: str | None = None,
|
||||
**kwargs,
|
||||
) -> InteractionsAPIResponse | AsyncIterator[InteractionsAPIStreamingResponse]:
|
||||
"""
|
||||
|
|
@ -129,6 +130,8 @@ async def acreate(
|
|||
response_format: JSON schema for response format
|
||||
response_mime_type: MIME type of the response
|
||||
previous_interaction_id: ID of previous interaction for continuation
|
||||
continuation_token: Token from an interaction that stopped at the per-request output
|
||||
cap, sent back to resume that decode
|
||||
extra_headers: Additional headers
|
||||
extra_body: Additional body parameters
|
||||
timeout: Request timeout
|
||||
|
|
@ -164,6 +167,7 @@ async def acreate(
|
|||
response_format=response_format,
|
||||
response_mime_type=response_mime_type,
|
||||
previous_interaction_id=previous_interaction_id,
|
||||
continuation_token=continuation_token,
|
||||
extra_headers=extra_headers,
|
||||
extra_body=extra_body,
|
||||
timeout=timeout,
|
||||
|
|
@ -232,6 +236,7 @@ def create(
|
|||
custom_llm_provider: str | None = None,
|
||||
*,
|
||||
agent_config: Mapping[str, object] | None = None,
|
||||
continuation_token: str | None = None,
|
||||
**kwargs,
|
||||
) -> (
|
||||
InteractionsAPIResponse
|
||||
|
|
@ -262,6 +267,8 @@ def create(
|
|||
response_format: JSON schema for response format
|
||||
response_mime_type: MIME type of the response
|
||||
previous_interaction_id: ID of previous interaction for continuation
|
||||
continuation_token: Token from an interaction that stopped at the per-request output
|
||||
cap, sent back to resume that decode
|
||||
extra_headers: Additional headers
|
||||
extra_body: Additional body parameters
|
||||
timeout: Request timeout
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ INTERACTIONS_API_OPTIONAL_PARAMS: Final = {
|
|||
"response_format",
|
||||
"response_mime_type",
|
||||
"previous_interaction_id",
|
||||
"continuation_token",
|
||||
"agent_config",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ class GoogleAIStudioInteractionsConfig(BaseInteractionsAPIConfig):
|
|||
"response_format",
|
||||
"response_mime_type",
|
||||
"previous_interaction_id",
|
||||
"continuation_token",
|
||||
]
|
||||
|
||||
def validate_environment(
|
||||
|
|
@ -206,6 +207,7 @@ class GoogleAIStudioInteractionsConfig(BaseInteractionsAPIConfig):
|
|||
"environment",
|
||||
"response_modalities",
|
||||
"previous_interaction_id",
|
||||
"continuation_token",
|
||||
]
|
||||
dropped_keys: Final = (
|
||||
frozenset({"agent_config"})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ _MODEL_BODY: Final = {
|
|||
"response_mime_type": "application/json",
|
||||
"response_format": {"type": "object", "properties": {"plan": {"type": "string"}}},
|
||||
"previous_interaction_id": "interaction-prev-123",
|
||||
"continuation_token": "Y29udGludWUtZnJvbS1oZXJl",
|
||||
"store": True,
|
||||
"background": True,
|
||||
}
|
||||
|
|
@ -120,6 +121,7 @@ def test_interaction_forwards_native_body_to_gemini(gateway: Gateway, prefix: st
|
|||
"store": True,
|
||||
"background": True,
|
||||
"previous_interaction_id": "interaction-prev-123",
|
||||
"continuation_token": "Y29udGludWUtZnJvbS1oZXJl",
|
||||
"generation_config": {"temperature": 0.3},
|
||||
"response_format": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -559,29 +559,76 @@ class TestInteractionsAcreateOffline:
|
|||
|
||||
@pytest.mark.usefixtures("fake_provider_credentials")
|
||||
@pytest.mark.asyncio
|
||||
async def test_acreate_gemini_keeps_continuation_token_out_of_the_request(self, respx_mock: MockRouter) -> None:
|
||||
async def test_acreate_gemini_resumes_with_the_token_it_handed_the_caller(self, respx_mock: MockRouter) -> None:
|
||||
"""The token of a cut interaction comes back on the response and reaches Google on the resend.
|
||||
|
||||
continuation_token is an input of CreateModelInteractionParams and an output of Interaction in
|
||||
https://ai.google.dev/static/api/interactions.openapi.json, read 2026-10-30
|
||||
"""
|
||||
cut: Final = {
|
||||
"id": "interaction-cut",
|
||||
"object": "interaction",
|
||||
"model": "gemini-3.8-flash",
|
||||
"status": "requires_continuation",
|
||||
"steps": [{"type": "model_output", "content": [{"type": "text", "text": "Rivers"}]}],
|
||||
"usage": {"input_tokens": 6, "output_tokens": 3},
|
||||
"continuation_token": "dG9rLTE=",
|
||||
}
|
||||
rest: Final = {**cut, "id": "interaction-rest", "status": "completed", "continuation_token": None}
|
||||
route: Final = respx_mock.post("https://generativelanguage.googleapis.com/v1beta/interactions").mock(
|
||||
side_effect=[httpx.Response(200, json=cut), httpx.Response(200, json=rest)]
|
||||
)
|
||||
first: Final = await interactions.acreate(
|
||||
model="gemini/gemini-3.8-flash",
|
||||
input="Prove it",
|
||||
api_key="gemini-offline",
|
||||
)
|
||||
assert first.status == "requires_continuation"
|
||||
assert first.continuation_token == "dG9rLTE="
|
||||
assert "continuation_token" not in json.loads(route.calls[0].request.content)
|
||||
|
||||
resumed: Final = await interactions.acreate(
|
||||
model="gemini/gemini-3.8-flash",
|
||||
input="Prove it",
|
||||
continuation_token=first.continuation_token,
|
||||
api_key="gemini-offline",
|
||||
)
|
||||
assert json.loads(route.calls.last.request.content) == {
|
||||
"model": "gemini-3.8-flash",
|
||||
"input": "Prove it",
|
||||
"continuation_token": "dG9rLTE=",
|
||||
}
|
||||
assert resumed.status == "completed"
|
||||
|
||||
@pytest.mark.usefixtures("fake_provider_credentials")
|
||||
@pytest.mark.asyncio
|
||||
async def test_acreate_gemini_binds_the_fifteenth_positional_argument_to_extra_headers(
|
||||
self, respx_mock: MockRouter
|
||||
) -> None:
|
||||
"""A caller passing model, agent, input and the eleven params after them positionally still
|
||||
lands its headers in ``extra_headers``, so adding a create parameter cannot shift that slot."""
|
||||
route: Final = respx_mock.post("https://generativelanguage.googleapis.com/v1beta/interactions").mock(
|
||||
return_value=httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"id": "interaction-offline",
|
||||
"id": "interaction-positional",
|
||||
"object": "interaction",
|
||||
"model": "gemini-3.8-flash",
|
||||
"status": "completed",
|
||||
"steps": [{"type": "model_output", "content": [{"type": "text", "text": "Hi"}]}],
|
||||
"usage": {"input_tokens": 6, "output_tokens": 3},
|
||||
},
|
||||
)
|
||||
)
|
||||
response: Final = await interactions.acreate(
|
||||
model="gemini/gemini-3.8-flash",
|
||||
input="Prove it",
|
||||
continuation_token="dG9rLTE=",
|
||||
await interactions.acreate(
|
||||
"gemini/gemini-3.8-flash",
|
||||
None,
|
||||
"Prove it",
|
||||
*([None] * 11),
|
||||
{"X-Trace-Id": "trace-9"},
|
||||
api_key="gemini-offline",
|
||||
)
|
||||
# The Interactions API answered 400 "Unknown parameter 'continuation_token'." on 2026-10-13
|
||||
assert route.calls.last.request.headers["x-trace-id"] == "trace-9"
|
||||
assert json.loads(route.calls.last.request.content) == {"model": "gemini-3.8-flash", "input": "Prove it"}
|
||||
assert response.status == "completed"
|
||||
|
||||
@pytest.mark.usefixtures("fake_provider_credentials")
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue