agentle 0.9.22__py3-none-any.whl → 0.9.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentle/agents/agent.py +175 -10
- agentle/agents/agent_run_output.py +8 -1
- agentle/generations/providers/google/adapters/generate_generate_content_response_to_generation_adapter.py +13 -10
- agentle/generations/providers/google/google_generation_provider.py +35 -5
- agentle/utils/parse_streaming_json.py +39 -13
- {agentle-0.9.22.dist-info → agentle-0.9.24.dist-info}/METADATA +1 -1
- {agentle-0.9.22.dist-info → agentle-0.9.24.dist-info}/RECORD +9 -9
- {agentle-0.9.22.dist-info → agentle-0.9.24.dist-info}/WHEEL +0 -0
- {agentle-0.9.22.dist-info → agentle-0.9.24.dist-info}/licenses/LICENSE +0 -0
agentle/agents/agent.py
CHANGED
|
@@ -1733,6 +1733,9 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
1733
1733
|
parsed=generation_chunk.parsed
|
|
1734
1734
|
if hasattr(generation_chunk, "parsed")
|
|
1735
1735
|
else cast(T_Schema, None),
|
|
1736
|
+
generation_text=generation_chunk.text
|
|
1737
|
+
if generation_chunk
|
|
1738
|
+
else "",
|
|
1736
1739
|
is_streaming_chunk=True,
|
|
1737
1740
|
is_final_chunk=False,
|
|
1738
1741
|
performance_metrics=partial_metrics,
|
|
@@ -1837,6 +1840,9 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
1837
1840
|
generation=final_generation,
|
|
1838
1841
|
context=context,
|
|
1839
1842
|
parsed=final_generation.parsed,
|
|
1843
|
+
generation_text=final_generation.text
|
|
1844
|
+
if final_generation
|
|
1845
|
+
else "",
|
|
1840
1846
|
is_streaming_chunk=False,
|
|
1841
1847
|
is_final_chunk=True,
|
|
1842
1848
|
performance_metrics=performance_metrics,
|
|
@@ -1953,6 +1959,7 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
1953
1959
|
generation=generation,
|
|
1954
1960
|
context=context,
|
|
1955
1961
|
parsed=generation.parsed,
|
|
1962
|
+
generation_text=generation.text if generation else "",
|
|
1956
1963
|
performance_metrics=performance_metrics,
|
|
1957
1964
|
)
|
|
1958
1965
|
|
|
@@ -2152,6 +2159,9 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
2152
2159
|
parsed=generation_chunk.parsed
|
|
2153
2160
|
if hasattr(generation_chunk, "parsed")
|
|
2154
2161
|
else cast(T_Schema, None),
|
|
2162
|
+
generation_text=generation_chunk.text
|
|
2163
|
+
if generation_chunk
|
|
2164
|
+
else "",
|
|
2155
2165
|
is_streaming_chunk=True,
|
|
2156
2166
|
is_final_chunk=False,
|
|
2157
2167
|
performance_metrics=partial_metrics,
|
|
@@ -2698,8 +2708,9 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
2698
2708
|
generation=None,
|
|
2699
2709
|
context=context,
|
|
2700
2710
|
parsed=cast(T_Schema, None),
|
|
2711
|
+
generation_text="",
|
|
2701
2712
|
is_suspended=True,
|
|
2702
|
-
suspension_reason=
|
|
2713
|
+
suspension_reason=suspension_reason,
|
|
2703
2714
|
resumption_token=resumption_token,
|
|
2704
2715
|
performance_metrics=performance_metrics,
|
|
2705
2716
|
)
|
|
@@ -3065,7 +3076,8 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
3065
3076
|
step_metrics=step_metrics,
|
|
3066
3077
|
average_generation_time_ms=generation_time_total
|
|
3067
3078
|
/ max(
|
|
3068
|
-
1,
|
|
3079
|
+
1,
|
|
3080
|
+
len([s for s in step_metrics if s.step_type == "generation"]),
|
|
3069
3081
|
),
|
|
3070
3082
|
average_tool_execution_time_ms=tool_execution_time_total
|
|
3071
3083
|
/ max(1, tool_calls_count),
|
|
@@ -3377,6 +3389,7 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
3377
3389
|
generation=None,
|
|
3378
3390
|
context=context,
|
|
3379
3391
|
parsed=cast(T_Schema, None),
|
|
3392
|
+
generation_text="",
|
|
3380
3393
|
is_suspended=True,
|
|
3381
3394
|
suspension_reason=suspension_error.reason,
|
|
3382
3395
|
resumption_token=resumption_token,
|
|
@@ -3802,7 +3815,9 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
3802
3815
|
return Context(
|
|
3803
3816
|
message_history=[
|
|
3804
3817
|
developer_message,
|
|
3805
|
-
UserMessage(
|
|
3818
|
+
UserMessage(
|
|
3819
|
+
parts=[TextPart(text=f"```json\n{text}\n```")],
|
|
3820
|
+
),
|
|
3806
3821
|
]
|
|
3807
3822
|
)
|
|
3808
3823
|
except (ImportError, AttributeError):
|
|
@@ -3986,8 +4001,24 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
3986
4001
|
generation=None,
|
|
3987
4002
|
context=context,
|
|
3988
4003
|
parsed=cast(T_Schema, None),
|
|
3989
|
-
|
|
3990
|
-
|
|
4004
|
+
generation_text="",
|
|
4005
|
+
performance_metrics=PerformanceMetrics(
|
|
4006
|
+
total_execution_time_ms=0.0,
|
|
4007
|
+
input_processing_time_ms=0.0,
|
|
4008
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4009
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4010
|
+
generation_time_ms=0.0,
|
|
4011
|
+
tool_execution_time_ms=0.0,
|
|
4012
|
+
final_response_processing_time_ms=0.0,
|
|
4013
|
+
iteration_count=0,
|
|
4014
|
+
tool_calls_count=0,
|
|
4015
|
+
total_tokens_processed=0,
|
|
4016
|
+
cache_hit_rate=0.0,
|
|
4017
|
+
average_generation_time_ms=0.0,
|
|
4018
|
+
average_tool_execution_time_ms=0.0,
|
|
4019
|
+
longest_step_duration_ms=0.0,
|
|
4020
|
+
shortest_step_duration_ms=0.0,
|
|
4021
|
+
),
|
|
3991
4022
|
)
|
|
3992
4023
|
|
|
3993
4024
|
try:
|
|
@@ -4010,7 +4041,24 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4010
4041
|
generation=None,
|
|
4011
4042
|
context=context,
|
|
4012
4043
|
parsed=cast(T_Schema, None),
|
|
4013
|
-
|
|
4044
|
+
generation_text="",
|
|
4045
|
+
performance_metrics=PerformanceMetrics(
|
|
4046
|
+
total_execution_time_ms=0.0,
|
|
4047
|
+
input_processing_time_ms=0.0,
|
|
4048
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4049
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4050
|
+
generation_time_ms=0.0,
|
|
4051
|
+
tool_execution_time_ms=0.0,
|
|
4052
|
+
final_response_processing_time_ms=0.0,
|
|
4053
|
+
iteration_count=0,
|
|
4054
|
+
tool_calls_count=0,
|
|
4055
|
+
total_tokens_processed=0,
|
|
4056
|
+
cache_hit_rate=0.0,
|
|
4057
|
+
average_generation_time_ms=0.0,
|
|
4058
|
+
average_tool_execution_time_ms=0.0,
|
|
4059
|
+
longest_step_duration_ms=0.0,
|
|
4060
|
+
shortest_step_duration_ms=0.0,
|
|
4061
|
+
),
|
|
4014
4062
|
)
|
|
4015
4063
|
|
|
4016
4064
|
suspension_type = suspension_state.get("type", "unknown")
|
|
@@ -4050,8 +4098,24 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4050
4098
|
generation=None,
|
|
4051
4099
|
context=context,
|
|
4052
4100
|
parsed=cast(T_Schema, None),
|
|
4053
|
-
|
|
4054
|
-
|
|
4101
|
+
generation_text="",
|
|
4102
|
+
performance_metrics=PerformanceMetrics(
|
|
4103
|
+
total_execution_time_ms=0.0,
|
|
4104
|
+
input_processing_time_ms=0.0,
|
|
4105
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4106
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4107
|
+
generation_time_ms=0.0,
|
|
4108
|
+
tool_execution_time_ms=0.0,
|
|
4109
|
+
final_response_processing_time_ms=0.0,
|
|
4110
|
+
iteration_count=0,
|
|
4111
|
+
tool_calls_count=0,
|
|
4112
|
+
total_tokens_processed=0,
|
|
4113
|
+
cache_hit_rate=0.0,
|
|
4114
|
+
average_generation_time_ms=0.0,
|
|
4115
|
+
average_tool_execution_time_ms=0.0,
|
|
4116
|
+
longest_step_duration_ms=0.0,
|
|
4117
|
+
shortest_step_duration_ms=0.0,
|
|
4118
|
+
),
|
|
4055
4119
|
)
|
|
4056
4120
|
|
|
4057
4121
|
async def _resume_from_tool_suspension(
|
|
@@ -4063,6 +4127,7 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4063
4127
|
This handles the most common suspension scenario where a tool
|
|
4064
4128
|
raised ToolSuspensionError and required approval.
|
|
4065
4129
|
"""
|
|
4130
|
+
execution_start_time = time.perf_counter()
|
|
4066
4131
|
_logger = Maybe(logger if self.debug else None)
|
|
4067
4132
|
|
|
4068
4133
|
# Extract suspension state
|
|
@@ -4198,9 +4263,30 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4198
4263
|
generation=None,
|
|
4199
4264
|
context=context,
|
|
4200
4265
|
parsed=cast(T_Schema, None),
|
|
4266
|
+
generation_text="",
|
|
4201
4267
|
is_suspended=True,
|
|
4202
4268
|
suspension_reason=suspension_error.reason,
|
|
4203
4269
|
resumption_token=resumption_token,
|
|
4270
|
+
performance_metrics=PerformanceMetrics(
|
|
4271
|
+
total_execution_time_ms=(time.perf_counter() - execution_start_time)
|
|
4272
|
+
* 1000
|
|
4273
|
+
if execution_start_time
|
|
4274
|
+
else 24,
|
|
4275
|
+
input_processing_time_ms=0.0,
|
|
4276
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4277
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4278
|
+
generation_time_ms=0.0,
|
|
4279
|
+
tool_execution_time_ms=0.0,
|
|
4280
|
+
final_response_processing_time_ms=0.0,
|
|
4281
|
+
iteration_count=0,
|
|
4282
|
+
tool_calls_count=0,
|
|
4283
|
+
total_tokens_processed=0,
|
|
4284
|
+
cache_hit_rate=0.0,
|
|
4285
|
+
average_generation_time_ms=0.0,
|
|
4286
|
+
average_tool_execution_time_ms=0.0,
|
|
4287
|
+
longest_step_duration_ms=0.0,
|
|
4288
|
+
shortest_step_duration_ms=0.0,
|
|
4289
|
+
),
|
|
4204
4290
|
)
|
|
4205
4291
|
except Exception as e:
|
|
4206
4292
|
# Tool execution failed
|
|
@@ -4232,8 +4318,24 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4232
4318
|
generation=None,
|
|
4233
4319
|
context=context,
|
|
4234
4320
|
parsed=cast(T_Schema, None),
|
|
4235
|
-
|
|
4236
|
-
|
|
4321
|
+
generation_text="",
|
|
4322
|
+
performance_metrics=PerformanceMetrics(
|
|
4323
|
+
total_execution_time_ms=0.0,
|
|
4324
|
+
input_processing_time_ms=0.0,
|
|
4325
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4326
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4327
|
+
generation_time_ms=0.0,
|
|
4328
|
+
tool_execution_time_ms=0.0,
|
|
4329
|
+
final_response_processing_time_ms=0.0,
|
|
4330
|
+
iteration_count=0,
|
|
4331
|
+
tool_calls_count=0,
|
|
4332
|
+
total_tokens_processed=0,
|
|
4333
|
+
cache_hit_rate=0.0,
|
|
4334
|
+
average_generation_time_ms=0.0,
|
|
4335
|
+
average_tool_execution_time_ms=0.0,
|
|
4336
|
+
longest_step_duration_ms=0.0,
|
|
4337
|
+
shortest_step_duration_ms=0.0,
|
|
4338
|
+
),
|
|
4237
4339
|
)
|
|
4238
4340
|
|
|
4239
4341
|
# Complete the step and add to context
|
|
@@ -4319,6 +4421,26 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4319
4421
|
generation=generation,
|
|
4320
4422
|
context=context,
|
|
4321
4423
|
parsed=generation.parsed,
|
|
4424
|
+
generation_text=generation.text if generation else "",
|
|
4425
|
+
performance_metrics=PerformanceMetrics(
|
|
4426
|
+
total_execution_time_ms=0.0,
|
|
4427
|
+
input_processing_time_ms=0.0,
|
|
4428
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4429
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4430
|
+
generation_time_ms=0.0,
|
|
4431
|
+
tool_execution_time_ms=0.0,
|
|
4432
|
+
final_response_processing_time_ms=0.0,
|
|
4433
|
+
iteration_count=1,
|
|
4434
|
+
tool_calls_count=0,
|
|
4435
|
+
total_tokens_processed=generation.usage.total_tokens
|
|
4436
|
+
if generation
|
|
4437
|
+
else 0,
|
|
4438
|
+
cache_hit_rate=0.0,
|
|
4439
|
+
average_generation_time_ms=0.0,
|
|
4440
|
+
average_tool_execution_time_ms=0.0,
|
|
4441
|
+
longest_step_duration_ms=0.0,
|
|
4442
|
+
shortest_step_duration_ms=0.0,
|
|
4443
|
+
),
|
|
4322
4444
|
)
|
|
4323
4445
|
|
|
4324
4446
|
# Has tools, continue with tool execution loop
|
|
@@ -4349,6 +4471,7 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4349
4471
|
This method continues the standard tool execution loop from where
|
|
4350
4472
|
the agent left off, handling iterations and tool calls.
|
|
4351
4473
|
"""
|
|
4474
|
+
execution_start_time = time.perf_counter()
|
|
4352
4475
|
_logger = Maybe(logger if self.debug else None)
|
|
4353
4476
|
generation_provider = self.generation_provider
|
|
4354
4477
|
|
|
@@ -4473,6 +4596,26 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4473
4596
|
generation=generation,
|
|
4474
4597
|
context=context,
|
|
4475
4598
|
parsed=generation.parsed,
|
|
4599
|
+
generation_text=generation.text if generation else "",
|
|
4600
|
+
performance_metrics=PerformanceMetrics(
|
|
4601
|
+
total_execution_time_ms=0.0,
|
|
4602
|
+
input_processing_time_ms=0.0,
|
|
4603
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4604
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4605
|
+
generation_time_ms=0.0,
|
|
4606
|
+
tool_execution_time_ms=0.0,
|
|
4607
|
+
final_response_processing_time_ms=0.0,
|
|
4608
|
+
iteration_count=1,
|
|
4609
|
+
tool_calls_count=0,
|
|
4610
|
+
total_tokens_processed=generation.usage.total_tokens
|
|
4611
|
+
if generation
|
|
4612
|
+
else 0,
|
|
4613
|
+
cache_hit_rate=0.0,
|
|
4614
|
+
average_generation_time_ms=0.0,
|
|
4615
|
+
average_tool_execution_time_ms=0.0,
|
|
4616
|
+
longest_step_duration_ms=0.0,
|
|
4617
|
+
shortest_step_duration_ms=0.0,
|
|
4618
|
+
),
|
|
4476
4619
|
)
|
|
4477
4620
|
|
|
4478
4621
|
# Execute tools
|
|
@@ -4538,9 +4681,30 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4538
4681
|
generation=None,
|
|
4539
4682
|
context=context,
|
|
4540
4683
|
parsed=cast(T_Schema, None),
|
|
4684
|
+
generation_text="",
|
|
4541
4685
|
is_suspended=True,
|
|
4542
4686
|
suspension_reason=suspension_error.reason,
|
|
4543
4687
|
resumption_token=resumption_token,
|
|
4688
|
+
performance_metrics=PerformanceMetrics(
|
|
4689
|
+
total_execution_time_ms=(
|
|
4690
|
+
time.perf_counter() - execution_start_time
|
|
4691
|
+
)
|
|
4692
|
+
* 1000,
|
|
4693
|
+
input_processing_time_ms=0.0,
|
|
4694
|
+
static_knowledge_processing_time_ms=0.0,
|
|
4695
|
+
mcp_tools_preparation_time_ms=0.0,
|
|
4696
|
+
generation_time_ms=0.0,
|
|
4697
|
+
tool_execution_time_ms=0.0,
|
|
4698
|
+
final_response_processing_time_ms=0.0,
|
|
4699
|
+
iteration_count=current_iteration,
|
|
4700
|
+
tool_calls_count=0,
|
|
4701
|
+
total_tokens_processed=0,
|
|
4702
|
+
cache_hit_rate=0.0,
|
|
4703
|
+
average_generation_time_ms=0.0,
|
|
4704
|
+
average_tool_execution_time_ms=0.0,
|
|
4705
|
+
longest_step_duration_ms=0.0,
|
|
4706
|
+
shortest_step_duration_ms=0.0,
|
|
4707
|
+
),
|
|
4544
4708
|
)
|
|
4545
4709
|
|
|
4546
4710
|
# Complete step and continue
|
|
@@ -4625,6 +4789,7 @@ class Agent[T_Schema = WithoutStructuredOutput](BaseModel):
|
|
|
4625
4789
|
generation=generation,
|
|
4626
4790
|
context=context,
|
|
4627
4791
|
parsed=parsed,
|
|
4792
|
+
generation_text=generation.text if generation else "",
|
|
4628
4793
|
performance_metrics=performance_metrics,
|
|
4629
4794
|
)
|
|
4630
4795
|
|
|
@@ -87,7 +87,14 @@ class AgentRunOutput[T_StructuredOutput](BaseModel):
|
|
|
87
87
|
parsed: T_StructuredOutput
|
|
88
88
|
"""
|
|
89
89
|
Structured data extracted from the agent's response.
|
|
90
|
-
In streaming mode,
|
|
90
|
+
In streaming mode, contains incrementally parsed partial data in each chunk,
|
|
91
|
+
with complete data available in the final chunk.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
generation_text: str = Field(default="")
|
|
95
|
+
"""
|
|
96
|
+
The text response from the agent.
|
|
97
|
+
Returns empty string if execution is suspended or generation is None.
|
|
91
98
|
"""
|
|
92
99
|
|
|
93
100
|
is_suspended: bool = Field(default=False)
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
import logging
|
|
5
5
|
import uuid
|
|
6
|
-
from collections.abc import AsyncIterator
|
|
6
|
+
from collections.abc import AsyncGenerator, AsyncIterator
|
|
7
7
|
from logging import Logger
|
|
8
8
|
from typing import TYPE_CHECKING, Any, Literal, cast, overload
|
|
9
9
|
|
|
@@ -88,11 +88,11 @@ class GenerateGenerateContentResponseToGenerationAdapter[T](
|
|
|
88
88
|
@overload
|
|
89
89
|
def adapt(
|
|
90
90
|
self, _f: AsyncIterator["GenerateContentResponse"]
|
|
91
|
-
) ->
|
|
91
|
+
) -> AsyncGenerator[Generation[T], None]: ...
|
|
92
92
|
|
|
93
93
|
def adapt(
|
|
94
94
|
self, _f: "GenerateContentResponse | AsyncIterator[GenerateContentResponse]"
|
|
95
|
-
) -> Generation[T] |
|
|
95
|
+
) -> Generation[T] | AsyncGenerator[Generation[T], None]:
|
|
96
96
|
"""
|
|
97
97
|
Convert Google response(s) to Agentle Generation object(s).
|
|
98
98
|
|
|
@@ -214,7 +214,7 @@ class GenerateGenerateContentResponseToGenerationAdapter[T](
|
|
|
214
214
|
|
|
215
215
|
async def _adapt_streaming(
|
|
216
216
|
self, response_stream: AsyncIterator["GenerateContentResponse"]
|
|
217
|
-
) ->
|
|
217
|
+
) -> AsyncGenerator[Generation[T], None]:
|
|
218
218
|
"""Adapt a streaming response with proper text accumulation."""
|
|
219
219
|
generation_id = self.preferred_id or uuid.uuid4()
|
|
220
220
|
created_time = datetime.datetime.now()
|
|
@@ -253,16 +253,19 @@ class GenerateGenerateContentResponseToGenerationAdapter[T](
|
|
|
253
253
|
_all_parts.extend(_parts)
|
|
254
254
|
|
|
255
255
|
if _optional_model is not None:
|
|
256
|
-
|
|
257
|
-
|
|
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,
|
|
258
260
|
model=_optional_model,
|
|
259
261
|
)
|
|
260
|
-
|
|
262
|
+
# Cast the optional model back to T for use in the generation
|
|
263
|
+
final_parsed = cast(T, parsed_optional_model)
|
|
261
264
|
else:
|
|
262
|
-
|
|
265
|
+
final_parsed = None
|
|
263
266
|
|
|
264
|
-
#
|
|
265
|
-
|
|
267
|
+
# Also check if chunk has parsed attribute from Google API
|
|
268
|
+
elif hasattr(chunk, "parsed") and chunk.parsed is not None:
|
|
266
269
|
final_parsed = cast(T | None, chunk.parsed)
|
|
267
270
|
|
|
268
271
|
# Extract usage (usually only in final chunk)
|
|
@@ -182,6 +182,37 @@ class GoogleGenerationProvider(GenerationProvider):
|
|
|
182
182
|
"""
|
|
183
183
|
return "google"
|
|
184
184
|
|
|
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
|
+
|
|
185
216
|
async def stream_async[T = WithoutStructuredOutput](
|
|
186
217
|
self,
|
|
187
218
|
*,
|
|
@@ -190,7 +221,7 @@ class GoogleGenerationProvider(GenerationProvider):
|
|
|
190
221
|
response_schema: type[T] | None = None,
|
|
191
222
|
generation_config: GenerationConfig | GenerationConfigDict | None = None,
|
|
192
223
|
tools: Sequence[Tool] | None = None,
|
|
193
|
-
) -> AsyncGenerator[Generation[
|
|
224
|
+
) -> AsyncGenerator[Generation[T], None]:
|
|
194
225
|
from google.genai import types
|
|
195
226
|
|
|
196
227
|
if self._normalize_generation_config(generation_config).n > 1:
|
|
@@ -260,6 +291,7 @@ class GoogleGenerationProvider(GenerationProvider):
|
|
|
260
291
|
tools=_tools,
|
|
261
292
|
max_output_tokens=_generation_config.max_output_tokens,
|
|
262
293
|
response_schema=response_schema if bool(response_schema) else None,
|
|
294
|
+
response_mime_type="application/json" if bool(response_schema) else None,
|
|
263
295
|
automatic_function_calling=types.AutomaticFunctionCallingConfig(
|
|
264
296
|
disable=disable_function_calling,
|
|
265
297
|
maximum_remote_calls=maximum_remote_calls,
|
|
@@ -295,10 +327,8 @@ class GoogleGenerationProvider(GenerationProvider):
|
|
|
295
327
|
raise
|
|
296
328
|
|
|
297
329
|
# Create the response
|
|
298
|
-
response = GenerateGenerateContentResponseToGenerationAdapter[
|
|
299
|
-
|
|
300
|
-
](
|
|
301
|
-
response_schema=None,
|
|
330
|
+
response = GenerateGenerateContentResponseToGenerationAdapter[T](
|
|
331
|
+
response_schema=response_schema,
|
|
302
332
|
model=used_model,
|
|
303
333
|
).adapt(generate_content_response_stream)
|
|
304
334
|
|
|
@@ -21,8 +21,6 @@ def parse_streaming_json[T: BaseModel](potential_json: str | None, model: type[T
|
|
|
21
21
|
if potential_json is None:
|
|
22
22
|
return model()
|
|
23
23
|
|
|
24
|
-
# print(f"parsing: {potential_json}")
|
|
25
|
-
|
|
26
24
|
def find_json_boundaries(text: str) -> tuple[int, int]:
|
|
27
25
|
"""Find the start and potential end of JSON in the text."""
|
|
28
26
|
|
|
@@ -95,17 +93,32 @@ def parse_streaming_json[T: BaseModel](potential_json: str | None, model: type[T
|
|
|
95
93
|
# Remove any leading/trailing whitespace
|
|
96
94
|
json_str = json_str.strip()
|
|
97
95
|
|
|
98
|
-
# Fix missing closing quotes on string values (at the end)
|
|
99
|
-
# Look for patterns like: "key": "value without closing quote
|
|
100
|
-
json_str = re.sub(r':\s*"([^"]*?)(?:\s*[,}]|$)', r': "\1"', json_str)
|
|
101
|
-
|
|
102
|
-
# Fix missing closing quotes for keys
|
|
103
|
-
# Look for patterns like: "key without quotes:
|
|
104
|
-
json_str = re.sub(r'"([^"]*?)(?=\s*:)', r'"\1"', json_str)
|
|
105
|
-
|
|
106
96
|
# Remove trailing commas before closing braces/brackets
|
|
107
97
|
json_str = re.sub(r",\s*([}\]])", r"\1", json_str)
|
|
108
98
|
|
|
99
|
+
# For streaming JSON, we need to handle incomplete strings carefully
|
|
100
|
+
# Check if we have an unclosed string at the end
|
|
101
|
+
in_string = False
|
|
102
|
+
escape_next = False
|
|
103
|
+
last_quote_pos = -1
|
|
104
|
+
|
|
105
|
+
for i, char in enumerate(json_str):
|
|
106
|
+
if escape_next:
|
|
107
|
+
escape_next = False
|
|
108
|
+
continue
|
|
109
|
+
if char == '\\':
|
|
110
|
+
escape_next = True
|
|
111
|
+
continue
|
|
112
|
+
if char == '"':
|
|
113
|
+
in_string = not in_string
|
|
114
|
+
if in_string:
|
|
115
|
+
last_quote_pos = i
|
|
116
|
+
|
|
117
|
+
# If we're in a string at the end (incomplete), close it properly
|
|
118
|
+
if in_string and last_quote_pos != -1:
|
|
119
|
+
# Add closing quote for the incomplete string
|
|
120
|
+
json_str += '"'
|
|
121
|
+
|
|
109
122
|
# Ensure the JSON has proper closing braces if it appears incomplete
|
|
110
123
|
open_braces = json_str.count("{") - json_str.count("}")
|
|
111
124
|
open_brackets = json_str.count("[") - json_str.count("]")
|
|
@@ -124,12 +137,25 @@ def parse_streaming_json[T: BaseModel](potential_json: str | None, model: type[T
|
|
|
124
137
|
data = {}
|
|
125
138
|
|
|
126
139
|
# Extract string key-value pairs with quoted keys
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
# IMPROVED: Handle long strings that may contain newlines, special chars, etc.
|
|
141
|
+
# Pattern: "key": "value..." - capture everything until the next unescaped quote or EOF
|
|
142
|
+
string_pattern = r'["\']([\w]+)["\']:\s*["\']([^"\']*?)(?:["\']|$)'
|
|
143
|
+
string_matches = re.findall(string_pattern, json_str, re.DOTALL)
|
|
144
|
+
|
|
145
|
+
# Also try to capture very long strings that span multiple lines
|
|
146
|
+
# This catches incomplete strings during streaming
|
|
147
|
+
long_string_pattern = r'["\']([\w_]+)["\']:\s*["\'](.+?)(?:["\'],?\s*["}]|$)'
|
|
148
|
+
long_matches = re.findall(long_string_pattern, json_str, re.DOTALL)
|
|
130
149
|
|
|
131
150
|
for key, value in string_matches:
|
|
132
151
|
data[key] = value
|
|
152
|
+
|
|
153
|
+
# Prefer long_matches for fields that might be truncated in string_matches
|
|
154
|
+
for key, value in long_matches:
|
|
155
|
+
# Only override if the long match has more content
|
|
156
|
+
existing = data.get(key, "")
|
|
157
|
+
if key not in data or (isinstance(existing, str) and len(value) > len(existing)):
|
|
158
|
+
data[key] = value
|
|
133
159
|
|
|
134
160
|
# Extract string key-value pairs with unquoted keys
|
|
135
161
|
# Pattern: key: "value" (no quotes around key)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
agentle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
agentle/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
agentle/agents/__init__.py,sha256=Y_Vh4954BlEehGOufH79lCvWlgDaVPN6TF9bo4OXioY,10
|
|
4
|
-
agentle/agents/agent.py,sha256=
|
|
4
|
+
agentle/agents/agent.py,sha256=e5tiq87bBfGI8gp5FU8mIVlxEs81GUpg-JTkptA0EeU,213449
|
|
5
5
|
agentle/agents/agent_config.py,sha256=tkssVo-cmldiQ6rDDkJ2xeK5NRIG3F-NgVhlXZGwCbM,4555
|
|
6
6
|
agentle/agents/agent_config_dict.py,sha256=BzGyKdm0Hv86P0_r7xzK4HtzNVdwM0HtrtuDVCSGbI8,2813
|
|
7
7
|
agentle/agents/agent_input.py,sha256=qckT3_pOGHclT8UViOjUeKCuGo0qigB_h6tYxOtwTXs,1447
|
|
8
8
|
agentle/agents/agent_pipeline.py,sha256=YJAMoHUfcChiwYLwe_vTXaS2QCkgq3DAThsjb0WxA_A,17160
|
|
9
9
|
agentle/agents/agent_protocol.py,sha256=7UTKSLwoM37zgWNWZxSPgEpBctPAxA4Zqiz4qiq6yCM,771
|
|
10
|
-
agentle/agents/agent_run_output.py,sha256=
|
|
10
|
+
agentle/agents/agent_run_output.py,sha256=UsFT48FsPlzWT8aQgNfajkt7IjXBbugI9EbRaXQ40tA,22669
|
|
11
11
|
agentle/agents/agent_team.py,sha256=bUQzLMhOp2UXmj1XhevqB0ZBqqzkhrTAUNV3BwSv7hc,30193
|
|
12
12
|
agentle/agents/context.py,sha256=ap8gSTqiBvYGrAegPOE5O2rz_dWPbiAgOJ5uahi1e0M,16391
|
|
13
13
|
agentle/agents/context_state.py,sha256=4EMxfuRvmAkNbH1ImdoP7MDFQSm9NYORsaCSaAicus0,135
|
|
@@ -316,10 +316,10 @@ agentle/generations/providers/failover/__init__.py,sha256=VBv_ZZlBFwwBRD2PZUEKi2
|
|
|
316
316
|
agentle/generations/providers/failover/failover_generation_provider.py,sha256=ixZk1zAP_Gu3HTf6nSo_ZDTl91U6x8vR0AyNHKB2b8k,24562
|
|
317
317
|
agentle/generations/providers/google/__init__.py,sha256=6H9LKSrN7Xyz5ZJ3PI4DIwOsAi8A84YoLSO9ms67cYI,105
|
|
318
318
|
agentle/generations/providers/google/function_calling_config.py,sha256=5ZL1FDCfeVVe9a04BpHC0_nBlLhmWg9nGoHVvCXNcI4,1226
|
|
319
|
-
agentle/generations/providers/google/google_generation_provider.py,sha256=
|
|
319
|
+
agentle/generations/providers/google/google_generation_provider.py,sha256=9Dl8D7npzpHL7qwO7Mgqiu6fzmWzPh-7jfvB6ZuUdYI,25590
|
|
320
320
|
agentle/generations/providers/google/adapters/__init__.py,sha256=sUwLR-CDzvWHn6hwqSsHYztws6DGx6ZiKp32vYeVMfk,1003
|
|
321
321
|
agentle/generations/providers/google/adapters/agentle_tool_to_google_tool_adapter.py,sha256=XQM_aL5zVfrRbNwtO_lDwXqvT8iX83trDNbEPSjZLmI,28482
|
|
322
|
-
agentle/generations/providers/google/adapters/generate_generate_content_response_to_generation_adapter.py,sha256=
|
|
322
|
+
agentle/generations/providers/google/adapters/generate_generate_content_response_to_generation_adapter.py,sha256=akzh75PmFnRF3X4L0QmF-lRokOTB9PR0ZFBv7nPTB3A,17234
|
|
323
323
|
agentle/generations/providers/google/adapters/google_content_to_generated_assistant_message_adapter.py,sha256=4sOcT9VLr58YfEnkOm392odhNzzmbTspnnmrz5K6bHc,7829
|
|
324
324
|
agentle/generations/providers/google/adapters/google_part_to_part_adapter.py,sha256=infqHs_LVjpzIOipIVbRHQesXTKoOHVLiTUFaPhaBV8,6520
|
|
325
325
|
agentle/generations/providers/google/adapters/message_to_google_content_adapter.py,sha256=sJ6MEn_R-KkLTEsNhxmKhBDbzlKwOUYqinydag6hqi4,6071
|
|
@@ -960,7 +960,7 @@ agentle/utils/file_validation.py,sha256=Rv6y_ylBrDoslscCk2lkfr3LRMarY0J8qWhKJjZj
|
|
|
960
960
|
agentle/utils/fix_base64_padding.py,sha256=IWBlMP3FYcWs17Y72tVxG7w1lzEKnstaDC9HRKmI56Y,1109
|
|
961
961
|
agentle/utils/make_fields_optional.py,sha256=pu2u-jJ1pNyoQRyGQzxwXnYbWSzZ2G4ssebJvjYsH8A,7800
|
|
962
962
|
agentle/utils/needs.py,sha256=JRTVvgVP0w0bpq1_w2Jly2lxXiONdHOYAYMvReFQcJM,5110
|
|
963
|
-
agentle/utils/parse_streaming_json.py,sha256=
|
|
963
|
+
agentle/utils/parse_streaming_json.py,sha256=9otgyfU37F-X9qbWHxgFmoYi9MSa9PPCarJ5CJ1Tcr0,31890
|
|
964
964
|
agentle/utils/raise_error.py,sha256=EYX6WJS5gzJxgTyFYYOFHc-klEyU0tP20N5k_-ze5nQ,138
|
|
965
965
|
agentle/utils/safe_b64decode.py,sha256=2nkdwUzkeVgtf3kb_zypjJY2KHe7dlLwa4ynjY2Yy1E,1272
|
|
966
966
|
agentle/utils/safe_dill_dumps.py,sha256=yXQ51P05uSiWnREKJ6UWVjCKnRQTjgPVzoE1nylBbJI,237
|
|
@@ -1007,7 +1007,7 @@ agentle/web/actions/scroll.py,sha256=WqVVAORNDK3BL1oASZBPmXJYeSVkPgAOmWA8ibYO82I
|
|
|
1007
1007
|
agentle/web/actions/viewport.py,sha256=KCwm88Pri19Qc6GLHC69HsRxmdJz1gEEAODfggC_fHo,287
|
|
1008
1008
|
agentle/web/actions/wait.py,sha256=IKEywjf-KC4ni9Gkkv4wgc7bY-hk7HwD4F-OFWlyf2w,571
|
|
1009
1009
|
agentle/web/actions/write_text.py,sha256=9mxfHcpKs_L7BsDnJvOYHQwG8M0GWe61SRJAsKk3xQ8,748
|
|
1010
|
-
agentle-0.9.
|
|
1011
|
-
agentle-0.9.
|
|
1012
|
-
agentle-0.9.
|
|
1013
|
-
agentle-0.9.
|
|
1010
|
+
agentle-0.9.24.dist-info/METADATA,sha256=AiMToA7H-QGVxwlLP3F5mPygDmgFdS0R0_KcSX83NHo,86849
|
|
1011
|
+
agentle-0.9.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
1012
|
+
agentle-0.9.24.dist-info/licenses/LICENSE,sha256=T90S9vqRS6qP-voULxAcvwEs558wRRo6dHuZrjgcOUI,1085
|
|
1013
|
+
agentle-0.9.24.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|