Skip to content

Commit 6b422cd

Browse files
committed
fix(google_provider): wrongly setting "parsed" attribute on generation
1 parent 81d6968 commit 6b422cd

File tree

3 files changed

+79
-41
lines changed

3 files changed

+79
-41
lines changed

agentle/generations/providers/google/adapters/generate_generate_content_response_to_generation_adapter.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,19 @@ async def _adapt_streaming(
253253
_all_parts.extend(_parts)
254254

255255
if _optional_model is not None:
256-
optional_model = parse_streaming_json(
257-
"".join([str(p.text) for p in _all_parts]),
256+
# Parse streaming JSON and update final_parsed
257+
accumulated_json_text = "".join([str(p.text) for p in _all_parts])
258+
parsed_optional_model = parse_streaming_json(
259+
accumulated_json_text,
258260
model=_optional_model,
259261
)
260-
chunk.parsed = optional_model
262+
# Cast the optional model back to T for use in the generation
263+
final_parsed = cast(T, parsed_optional_model)
261264
else:
262-
chunk.parsed = None
265+
final_parsed = None
263266

264-
# Extract parsed data (usually only available in final chunk)
265-
if hasattr(chunk, "parsed") and chunk.parsed is not None:
267+
# Also check if chunk has parsed attribute from Google API
268+
elif hasattr(chunk, "parsed") and chunk.parsed is not None:
266269
final_parsed = cast(T | None, chunk.parsed)
267270

268271
# Extract usage (usually only in final chunk)

agentle/generations/providers/google/google_generation_provider.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,37 @@ def organization(self) -> str:
182182
"""
183183
return "google"
184184

185+
@overload
186+
def stream_async[T](
187+
self,
188+
*,
189+
model: str | ModelKind | None = None,
190+
messages: Sequence[Message],
191+
response_schema: type[T],
192+
generation_config: GenerationConfig | GenerationConfigDict | None = None,
193+
) -> AsyncGenerator[Generation[T], None]: ...
194+
195+
@overload
196+
def stream_async(
197+
self,
198+
*,
199+
model: str | ModelKind | None = None,
200+
messages: Sequence[Message],
201+
response_schema: None = None,
202+
generation_config: GenerationConfig | GenerationConfigDict | None = None,
203+
tools: Sequence[Tool],
204+
) -> AsyncGenerator[Generation[WithoutStructuredOutput], None]: ...
205+
206+
@overload
207+
def stream_async(
208+
self,
209+
*,
210+
model: str | ModelKind | None = None,
211+
messages: Sequence[Message],
212+
response_schema: None = None,
213+
generation_config: GenerationConfig | GenerationConfigDict | None = None,
214+
) -> AsyncGenerator[Generation[WithoutStructuredOutput], None]: ...
215+
185216
async def stream_async[T = WithoutStructuredOutput](
186217
self,
187218
*,
@@ -190,7 +221,7 @@ async def stream_async[T = WithoutStructuredOutput](
190221
response_schema: type[T] | None = None,
191222
generation_config: GenerationConfig | GenerationConfigDict | None = None,
192223
tools: Sequence[Tool] | None = None,
193-
) -> AsyncGenerator[Generation[WithoutStructuredOutput], None]:
224+
) -> AsyncGenerator[Generation[T], None]:
194225
from google.genai import types
195226

196227
if self._normalize_generation_config(generation_config).n > 1:
@@ -260,6 +291,7 @@ async def stream_async[T = WithoutStructuredOutput](
260291
tools=_tools,
261292
max_output_tokens=_generation_config.max_output_tokens,
262293
response_schema=response_schema if bool(response_schema) else None,
294+
response_mime_type="application/json" if bool(response_schema) else None,
263295
automatic_function_calling=types.AutomaticFunctionCallingConfig(
264296
disable=disable_function_calling,
265297
maximum_remote_calls=maximum_remote_calls,
@@ -295,10 +327,8 @@ async def stream_async[T = WithoutStructuredOutput](
295327
raise
296328

297329
# Create the response
298-
response = GenerateGenerateContentResponseToGenerationAdapter[
299-
WithoutStructuredOutput
300-
](
301-
response_schema=None,
330+
response = GenerateGenerateContentResponseToGenerationAdapter[T](
331+
response_schema=response_schema,
302332
model=used_model,
303333
).adapt(generate_content_response_stream)
304334

examples/providers.py

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,51 @@
44
This example demonstrates how to use different model providers with the Agentle framework.
55
"""
66

7+
import asyncio
78
from dotenv import load_dotenv
9+
from rsb.models.base_model import BaseModel
10+
811

9-
from agentle.generations.models.message_parts.file import FilePart
1012
from agentle.generations.models.message_parts.text import TextPart
1113
from agentle.generations.models.messages.user_message import UserMessage
1214
from agentle.generations.providers.base.generation_provider import GenerationProvider
13-
from agentle.generations.providers.openrouter.openrouter_generation_provider import (
14-
OpenRouterGenerationProvider,
15+
from agentle.generations.providers.google.google_generation_provider import (
16+
GoogleGenerationProvider,
1517
)
1618

1719
load_dotenv()
1820

1921

20-
def add_numbers(a: float, b: float) -> float:
21-
return a + b
22+
class PoemResponse(BaseModel):
23+
poem: str
2224

2325

2426
# Example 1: Create an agent with Google's Gemini model
25-
provider: GenerationProvider = OpenRouterGenerationProvider().with_fallback_models(
26-
"openai/gpt-5-nano"
27-
)
28-
29-
example_file = FilePart.from_local_file(
30-
"/Users/arthurbrenno/Documents/Dev/Paragon/agentle/examples/harvard.wav",
31-
"audio/mp3",
32-
)
33-
34-
# Run the Google agent
35-
generation = provider.generate(
36-
model="google/gemini-2.5-flash",
37-
messages=[
38-
UserMessage(
39-
parts=[
40-
example_file,
41-
TextPart(
42-
text="O que tem nesse audio?",
43-
),
44-
]
45-
)
46-
],
47-
)
48-
49-
print(generation)
27+
provider: GenerationProvider = GoogleGenerationProvider()
28+
29+
30+
async def main() -> None:
31+
# Run the Google agent
32+
stream = provider.stream_async(
33+
model="google/gemini-2.5-flash",
34+
messages=[
35+
UserMessage(
36+
parts=[
37+
TextPart(
38+
text="Escreva um poema sobre o amor",
39+
),
40+
]
41+
)
42+
],
43+
response_schema=PoemResponse,
44+
)
45+
46+
async for generation in stream:
47+
print(generation)
48+
print()
49+
print()
50+
print()
51+
52+
53+
if __name__ == "__main__":
54+
asyncio.run(main())

0 commit comments

Comments
 (0)