pyagentic-core 2.0.2__tar.gz → 2.1.0__tar.gz
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.
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/PKG-INFO +1 -1
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_agent/_agent.py +119 -65
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_agent/_agent_state.py +3 -1
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/models/response.py +3 -1
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic_core.egg-info/PKG-INFO +1 -1
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyproject.toml +1 -1
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/LICENSE +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/README.md +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/__init__.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/__init__.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_agent/__init__.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_agent/_agent_linking.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_exceptions.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_info.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_metaclasses.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_ref.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_spec.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_state.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_tool.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_base/_validation.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_utils/_typing.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/_utils/_warnings.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/__init__.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/_anthropic.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/_gemini.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/_mock.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/_openai.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/_openaiv1.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/llm/_provider.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/logging.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/models/llm.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/models/tracing.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/policies/__init__.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/policies/_events.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/policies/_policy.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/tracing/__init__.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/tracing/_basic.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/tracing/_langfuse.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/tracing/_tracer.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic/updates.py +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic_core.egg-info/SOURCES.txt +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic_core.egg-info/dependency_links.txt +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic_core.egg-info/requires.txt +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/pyagentic_core.egg-info/top_level.txt +0 -0
- {pyagentic_core-2.0.2 → pyagentic_core-2.1.0}/setup.cfg +0 -0
|
@@ -4,13 +4,13 @@ from functools import wraps
|
|
|
4
4
|
from typing import (
|
|
5
5
|
Callable,
|
|
6
6
|
Any,
|
|
7
|
-
|
|
7
|
+
AsyncGenerator,
|
|
8
8
|
ClassVar,
|
|
9
9
|
Type,
|
|
10
|
-
Self,
|
|
11
10
|
dataclass_transform,
|
|
12
11
|
Optional,
|
|
13
12
|
TYPE_CHECKING,
|
|
13
|
+
Union,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
from pydantic import BaseModel, ValidationError
|
|
@@ -126,8 +126,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
126
126
|
api_key (str, optional): API key matching the model provider
|
|
127
127
|
provider (LLMProvider, optional): Pre-configured provider instance. Overrides
|
|
128
128
|
`model` and `api_key` if provided.
|
|
129
|
-
emitter (Callable, optional): Callback function to receive real-time updates
|
|
130
|
-
about the agent's execution (useful for WebSocket streaming)
|
|
131
129
|
tracer (AgentTracer, optional): Tracer instance for observability. Defaults
|
|
132
130
|
to BasicTracer if not provided.
|
|
133
131
|
max_call_depth (int): Maximum number of tool calling loops per run. Defaults to 1.
|
|
@@ -177,7 +175,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
177
175
|
model: str = None
|
|
178
176
|
api_key: str = None
|
|
179
177
|
provider: LLMProvider = None
|
|
180
|
-
emitter: Callable[[Any], str] = None
|
|
181
178
|
tracer: AgentTracer = None
|
|
182
179
|
max_call_depth: int = 1
|
|
183
180
|
|
|
@@ -314,8 +311,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
314
311
|
except Exception as e:
|
|
315
312
|
# Handle inference errors gracefully
|
|
316
313
|
logger.exception(e)
|
|
317
|
-
if self.emitter:
|
|
318
|
-
await _safe_run(self.emitter, EmitUpdate(status=Status.ERROR))
|
|
319
314
|
# Add error message to conversation history
|
|
320
315
|
self.state._messages.append(
|
|
321
316
|
Message(role="assistant", content="Failed to generate a response")
|
|
@@ -359,8 +354,13 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
359
354
|
response = AgentResponse(final_output=result, provider_info=agent.provider._info)
|
|
360
355
|
|
|
361
356
|
# Add agent result to conversation history
|
|
357
|
+
stringified_result = (
|
|
358
|
+
result.model_dump_json(indent=2)
|
|
359
|
+
if issubclass(result.__class__, BaseModel)
|
|
360
|
+
else str(result)
|
|
361
|
+
)
|
|
362
362
|
self.state._messages.append(
|
|
363
|
-
self.provider.to_tool_call_result_message(result=
|
|
363
|
+
self.provider.to_tool_call_result_message(result=stringified_result, id_=tool_call.id)
|
|
364
364
|
)
|
|
365
365
|
return response
|
|
366
366
|
|
|
@@ -397,38 +397,15 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
397
397
|
# Handle validation errors for tool arguments
|
|
398
398
|
result = f"Function Args were invalid: {str(e)}"
|
|
399
399
|
compiled_args = {}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
logger.exception(e)
|
|
403
|
-
if self.emitter:
|
|
404
|
-
await _safe_run(
|
|
405
|
-
self.emitter,
|
|
406
|
-
ToolUpdate(
|
|
407
|
-
status=Status.ERROR, tool_call=tool_call.name, tool_args=kwargs
|
|
408
|
-
),
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
# Execute the tool, emitting status updates
|
|
400
|
+
self.tracer.record_exception(str(e))
|
|
401
|
+
logger.exception(e)
|
|
412
402
|
try:
|
|
413
|
-
if self.emitter:
|
|
414
|
-
await _safe_run(
|
|
415
|
-
self.emitter,
|
|
416
|
-
ToolUpdate(
|
|
417
|
-
status=Status.PROCESSING, tool_call=tool_call.name, tool_args=kwargs
|
|
418
|
-
),
|
|
419
|
-
)
|
|
420
403
|
if compiled_args:
|
|
421
404
|
result = await _safe_run(handler, **compiled_args)
|
|
422
|
-
result = str(result)
|
|
423
405
|
self.tracer.set_attributes(result=result)
|
|
424
406
|
except TypeError as e:
|
|
425
407
|
self.tracer.record_exception(str(e))
|
|
426
408
|
logger.exception(e)
|
|
427
|
-
if self.emitter:
|
|
428
|
-
await _safe_run(
|
|
429
|
-
self.emitter,
|
|
430
|
-
ToolUpdate(status=Status.ERROR, tool_call=tool_call.name, tool_args=kwargs),
|
|
431
|
-
)
|
|
432
409
|
raise InvalidToolDefinition(
|
|
433
410
|
tool_name=tool_call.name,
|
|
434
411
|
message=f"Tool must have a serializable return type; {tool_def.return_type} failed to be casted to a string.",
|
|
@@ -438,15 +415,15 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
438
415
|
self.tracer.record_exception(str(e))
|
|
439
416
|
logger.exception(e)
|
|
440
417
|
result = f"Tool `{tool_call.name}` failed: {e}. Please kindly state to the user that is failed, provide state, and ask if they want to try again." # noqa E501
|
|
441
|
-
if self.emitter:
|
|
442
|
-
await _safe_run(
|
|
443
|
-
self.emitter,
|
|
444
|
-
ToolUpdate(status=Status.ERROR, tool_call=tool_call.name, tool_args=kwargs),
|
|
445
|
-
)
|
|
446
418
|
|
|
419
|
+
stringified_result = (
|
|
420
|
+
result.model_dump_json(indent=2)
|
|
421
|
+
if issubclass(result.__class__, BaseModel)
|
|
422
|
+
else str(result)
|
|
423
|
+
)
|
|
447
424
|
# Add tool result to conversation history for LLM
|
|
448
425
|
self.state._messages.append(
|
|
449
|
-
self.provider.to_tool_call_result_message(result=
|
|
426
|
+
self.provider.to_tool_call_result_message(result=stringified_result, id_=tool_call.id)
|
|
450
427
|
)
|
|
451
428
|
|
|
452
429
|
# Build and return the structured tool response
|
|
@@ -479,12 +456,16 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
479
456
|
|
|
480
457
|
return tool_defs
|
|
481
458
|
|
|
482
|
-
async def
|
|
459
|
+
async def step(
|
|
460
|
+
self, input_: str
|
|
461
|
+
) -> AsyncGenerator[Union[ToolResponse, AgentResponse, LLMResponse]]:
|
|
483
462
|
"""
|
|
484
|
-
|
|
485
|
-
|
|
463
|
+
Streams all intermediate responses as the agent executes. Yields LLMResponse for each
|
|
464
|
+
inference, ToolResponse for each tool execution, and finally AgentResponse with the
|
|
465
|
+
complete result.
|
|
486
466
|
|
|
487
|
-
|
|
467
|
+
This is the core execution method that enables real-time streaming and fine-grained
|
|
468
|
+
control over agent execution. The agent follows an agentic loop pattern:
|
|
488
469
|
1. Send user input and conversation history to the LLM
|
|
489
470
|
2. LLM decides to either call tools or respond with final output
|
|
490
471
|
3. If tools are called, execute them and feed results back to LLM
|
|
@@ -493,19 +474,23 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
493
474
|
Args:
|
|
494
475
|
input_ (str): The user input/query for the agent to process
|
|
495
476
|
|
|
496
|
-
|
|
497
|
-
AgentResponse:
|
|
498
|
-
-
|
|
499
|
-
-
|
|
500
|
-
-
|
|
501
|
-
- provider_info: Information about the LLM provider used
|
|
477
|
+
Yields:
|
|
478
|
+
Union[LLMResponse, ToolResponse, AgentResponse]: Responses in sequence:
|
|
479
|
+
- LLMResponse: Yielded each time the LLM is called (may happen multiple times)
|
|
480
|
+
- ToolResponse: Yielded for each tool execution
|
|
481
|
+
- AgentResponse: Final response with complete execution summary
|
|
502
482
|
|
|
503
483
|
Example:
|
|
504
484
|
```python
|
|
505
485
|
agent = MyAgent(model="openai::gpt-4o", api_key=API_KEY)
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
486
|
+
|
|
487
|
+
async for response in agent.step("Analyze this data"):
|
|
488
|
+
if isinstance(response, LLMResponse):
|
|
489
|
+
print(f"LLM thinking: {response.text}")
|
|
490
|
+
elif isinstance(response, ToolResponse):
|
|
491
|
+
print(f"Tool executed: {response.output}")
|
|
492
|
+
elif isinstance(response, AgentResponse):
|
|
493
|
+
print(f"Final: {response.final_output}")
|
|
509
494
|
```
|
|
510
495
|
"""
|
|
511
496
|
async with self.tracer.agent(
|
|
@@ -527,10 +512,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
527
512
|
agent_responses: list = []
|
|
528
513
|
processed_call_ids: set[str] = set()
|
|
529
514
|
|
|
530
|
-
# Emit initial status
|
|
531
|
-
if self.emitter:
|
|
532
|
-
await _safe_run(self.emitter, EmitUpdate(status=Status.GENERATING))
|
|
533
|
-
|
|
534
515
|
# Main agentic loop: LLM -> Tools -> LLM -> ...
|
|
535
516
|
depth = 0
|
|
536
517
|
final_ai_output: str | None = None
|
|
@@ -538,6 +519,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
538
519
|
while depth < self.max_call_depth:
|
|
539
520
|
# Ask the LLM what to do next (may return tool calls or final text)
|
|
540
521
|
response = await self._process_llm_inference(tool_defs=tool_defs)
|
|
522
|
+
yield response
|
|
541
523
|
|
|
542
524
|
# If the model produced final text without tool calls, we're done
|
|
543
525
|
if not response.tool_calls:
|
|
@@ -557,10 +539,12 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
557
539
|
if tool_call.name in self.__tool_defs__:
|
|
558
540
|
result = await self._process_tool_call(tool_call, call_depth=depth)
|
|
559
541
|
tool_responses.append(result)
|
|
542
|
+
yield result
|
|
560
543
|
|
|
561
544
|
elif tool_call.name in self.__linked_agents__:
|
|
562
545
|
result = await self._process_agent_call(tool_call)
|
|
563
546
|
agent_responses.append(result)
|
|
547
|
+
yield result
|
|
564
548
|
|
|
565
549
|
# Increment depth and continue loop (LLM will see tool results next iteration)
|
|
566
550
|
depth += 1
|
|
@@ -570,12 +554,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
570
554
|
response = await self._process_llm_inference()
|
|
571
555
|
final_ai_output = response.parsed if response.parsed else response.text
|
|
572
556
|
|
|
573
|
-
# Emit final success status
|
|
574
|
-
if self.emitter:
|
|
575
|
-
await _safe_run(
|
|
576
|
-
self.emitter, AiUpdate(status=Status.SUCCEDED, message=final_ai_output)
|
|
577
|
-
)
|
|
578
|
-
|
|
579
557
|
# Build the structured response
|
|
580
558
|
response_fields = {
|
|
581
559
|
"final_output": final_ai_output,
|
|
@@ -590,17 +568,93 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
590
568
|
|
|
591
569
|
response = self.__response_model__(**response_fields)
|
|
592
570
|
self.tracer.set_attributes(output=response)
|
|
593
|
-
|
|
571
|
+
yield response
|
|
572
|
+
|
|
573
|
+
async def run(self, input_: str) -> AgentResponse:
|
|
574
|
+
"""
|
|
575
|
+
Executes the agent with a message string and returns the final result.
|
|
576
|
+
|
|
577
|
+
This method consumes the entire step() generator and returns only the final
|
|
578
|
+
AgentResponse. Use this when you don't need intermediate streaming responses
|
|
579
|
+
and just want the final output.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
input_ (str): The user input/query for the agent to process
|
|
583
|
+
|
|
584
|
+
Returns:
|
|
585
|
+
AgentResponse: Structured response containing:
|
|
586
|
+
- final_output: The final text or structured output from the LLM
|
|
587
|
+
- state: Current agent state after execution
|
|
588
|
+
- tool_responses: List of all tool calls and their outputs
|
|
589
|
+
- agent_responses: List of linked agent calls (if any)
|
|
590
|
+
- provider_info: Information about the LLM provider used
|
|
591
|
+
|
|
592
|
+
Example:
|
|
593
|
+
```python
|
|
594
|
+
agent = MyAgent(model="openai::gpt-4o", api_key=API_KEY)
|
|
595
|
+
response = await agent.run("What's the weather in San Francisco?")
|
|
596
|
+
print(response.final_output) # LLM's final answer
|
|
597
|
+
print(response.tool_responses) # Tools that were called
|
|
598
|
+
```
|
|
599
|
+
"""
|
|
600
|
+
final_response = None
|
|
601
|
+
async for res in self.step(input_):
|
|
602
|
+
final_response = res
|
|
603
|
+
return final_response
|
|
594
604
|
|
|
595
605
|
async def __call__(self, user_input: str) -> BaseModel:
|
|
596
606
|
"""
|
|
597
|
-
|
|
607
|
+
Customizable callable interface for the agent. Override this method to accept
|
|
608
|
+
structured, typed parameters that match your agent's purpose.
|
|
609
|
+
|
|
610
|
+
When this agent is linked to another agent, the parameters of this method become
|
|
611
|
+
the tool parameters that the LLM sees. This enables type-safe, structured agent
|
|
612
|
+
composition in multi-agent systems.
|
|
613
|
+
|
|
614
|
+
The default implementation accepts a single user_input string and forwards it to
|
|
615
|
+
run(). Override to provide a custom interface:
|
|
598
616
|
|
|
599
617
|
Args:
|
|
600
|
-
user_input (str): The user input to process
|
|
618
|
+
user_input (str): The user input to process (default implementation)
|
|
601
619
|
|
|
602
620
|
Returns:
|
|
603
621
|
AgentResponse: The agent's response
|
|
622
|
+
|
|
623
|
+
Example (Default Usage):
|
|
624
|
+
```python
|
|
625
|
+
agent = MyAgent(model="openai::gpt-4o", api_key=API_KEY)
|
|
626
|
+
response = await agent("What's the weather?")
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Example (Custom Implementation):
|
|
630
|
+
```python
|
|
631
|
+
class CoursePlannerAgent(BaseAgent):
|
|
632
|
+
__system_message__ = "You design course curricula"
|
|
633
|
+
__description__ = "Creates structured course plans"
|
|
634
|
+
|
|
635
|
+
async def __call__(
|
|
636
|
+
self,
|
|
637
|
+
goal: str,
|
|
638
|
+
experience: str,
|
|
639
|
+
context: Optional[str] = None
|
|
640
|
+
) -> CoursePlan:
|
|
641
|
+
# Build structured prompt from parameters
|
|
642
|
+
prompt = f"Goal: {goal}\\nExperience: {experience}"
|
|
643
|
+
if context:
|
|
644
|
+
prompt += f"\\nContext: {context}"
|
|
645
|
+
return await self.run(prompt)
|
|
646
|
+
|
|
647
|
+
# Call with structured parameters
|
|
648
|
+
planner = CoursePlannerAgent(model="openai::gpt-4o", api_key=API_KEY)
|
|
649
|
+
course = await planner(
|
|
650
|
+
goal="Learn ML",
|
|
651
|
+
experience="Python beginner",
|
|
652
|
+
context="Prefer hands-on projects"
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
# When linked to another agent, the LLM sees:
|
|
656
|
+
# Tool: planner(goal: str, experience: str, context: Optional[str])
|
|
657
|
+
```
|
|
604
658
|
"""
|
|
605
659
|
return await self.run(input_=user_input)
|
|
606
660
|
|
|
@@ -32,7 +32,9 @@ class _AgentState(BaseModel):
|
|
|
32
32
|
input_template: Optional[str] = "{{ user_message }}"
|
|
33
33
|
_messages: list[Message] = PrivateAttr(default_factory=list)
|
|
34
34
|
_instructions_template: Template = PrivateAttr(default_factory=lambda: Template(source=""))
|
|
35
|
-
_input_template: Template = PrivateAttr(
|
|
35
|
+
_input_template: Template = PrivateAttr(
|
|
36
|
+
default_factory=lambda: Template(source="{{ user_message }}")
|
|
37
|
+
)
|
|
36
38
|
|
|
37
39
|
def model_post_init(self, state):
|
|
38
40
|
self._instructions_template = Template(source=self.instructions)
|
|
@@ -60,7 +60,9 @@ class ToolResponse(BaseModel):
|
|
|
60
60
|
case _:
|
|
61
61
|
raise Exception(f"Unsupported type: {param_type}")
|
|
62
62
|
|
|
63
|
-
return create_model(
|
|
63
|
+
return create_model(
|
|
64
|
+
f"ToolResponse[{tool_def.name}]", __base__=cls, output=tool_def.return_type, **fields
|
|
65
|
+
)
|
|
64
66
|
|
|
65
67
|
|
|
66
68
|
class AgentResponse(BaseModel):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|