fluxloop-cli 0.2.23__tar.gz → 0.2.25__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.
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/PKG-INFO +2 -1
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/__init__.py +1 -1
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/record.py +17 -10
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/conversation_supervisor.py +14 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/core.py +27 -3
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/runner.py +29 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/templates.py +1 -1
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli.egg-info/PKG-INFO +2 -1
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli.egg-info/requires.txt +1 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/pyproject.toml +2 -1
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/README.md +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/arg_binder.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/__init__.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/config.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/doctor.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/evaluate.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/generate.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/init.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/parse.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/run.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/commands/status.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/config_loader.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/config_schema.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/constants.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/environment.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/__init__.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/config.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/__init__.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/analysis.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/reporting/__init__.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/reporting/html.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/reporting/markdown.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/success.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/llm.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/__init__.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/base.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/information_completeness.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/intent_recognition.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/response_clarity.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/response_consistency.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/rules.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/input_generator.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/llm_generator.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/main.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/project_paths.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/target_loader.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/validators.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli.egg-info/SOURCES.txt +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli.egg-info/entry_points.txt +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli.egg-info/top_level.txt +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/setup.cfg +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_analysis_recommendations.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_arg_binder.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_config_command.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_evaluate_command.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_evaluation_llm.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_input_generator.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_prompt_library.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_run_command.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_success_criteria.py +0 -0
- {fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/tests/test_target_loader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fluxloop-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.25
|
|
4
4
|
Summary: FluxLoop CLI for running agent simulations
|
|
5
5
|
Author-email: FluxLoop Team <team@fluxloop.dev>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -26,6 +26,7 @@ Requires-Dist: httpx>=0.24.0
|
|
|
26
26
|
Requires-Dist: rich>=13.0
|
|
27
27
|
Requires-Dist: python-dotenv>=1.0.0
|
|
28
28
|
Requires-Dist: fluxloop>=0.1.0
|
|
29
|
+
Requires-Dist: ruamel.yaml>=0.17.0
|
|
29
30
|
Provides-Extra: dev
|
|
30
31
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
32
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -8,6 +8,7 @@ from typing import Dict, Optional
|
|
|
8
8
|
import typer
|
|
9
9
|
from rich.console import Console
|
|
10
10
|
from rich.table import Table
|
|
11
|
+
from ruamel.yaml import YAML, CommentedMap
|
|
11
12
|
|
|
12
13
|
from ..constants import DEFAULT_ROOT_DIR_NAME
|
|
13
14
|
from ..project_paths import (
|
|
@@ -20,6 +21,9 @@ from ..config_schema import CONFIG_SECTION_FILENAMES
|
|
|
20
21
|
|
|
21
22
|
app = typer.Typer()
|
|
22
23
|
console = Console()
|
|
24
|
+
_yaml = YAML()
|
|
25
|
+
_yaml.indent(mapping=2, sequence=4, offset=2)
|
|
26
|
+
_yaml.preserve_quotes = True
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
def _load_env(env_path: Path) -> Dict[str, str]:
|
|
@@ -44,22 +48,25 @@ def _update_simulation(recording_enabled: bool, project: Optional[str], root: Pa
|
|
|
44
48
|
except KeyError:
|
|
45
49
|
return
|
|
46
50
|
|
|
47
|
-
data: Dict[str, object] = {}
|
|
48
51
|
if simulation_path.exists():
|
|
49
|
-
|
|
52
|
+
with simulation_path.open("r", encoding="utf-8") as handle:
|
|
53
|
+
loaded = _yaml.load(handle) or CommentedMap()
|
|
54
|
+
else:
|
|
55
|
+
loaded = CommentedMap()
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
if not isinstance(loaded, CommentedMap):
|
|
58
|
+
loaded = CommentedMap(loaded)
|
|
52
59
|
|
|
53
|
-
replay =
|
|
54
|
-
replay
|
|
60
|
+
replay = loaded.get("replay_args")
|
|
61
|
+
if not isinstance(replay, CommentedMap):
|
|
62
|
+
replay = CommentedMap(replay or {})
|
|
63
|
+
loaded["replay_args"] = replay
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
replay["enabled"] = recording_enabled
|
|
57
66
|
|
|
58
67
|
simulation_path.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
-
simulation_path.
|
|
60
|
-
|
|
61
|
-
encoding="utf-8",
|
|
62
|
-
)
|
|
68
|
+
with simulation_path.open("w", encoding="utf-8") as handle:
|
|
69
|
+
_yaml.dump(loaded, handle)
|
|
63
70
|
|
|
64
71
|
|
|
65
72
|
@app.command()
|
|
@@ -9,6 +9,7 @@ next user utterance consistent with the experiment persona and service context.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import json
|
|
12
|
+
import logging
|
|
12
13
|
import os
|
|
13
14
|
import re
|
|
14
15
|
from dataclasses import dataclass
|
|
@@ -18,6 +19,7 @@ import httpx
|
|
|
18
19
|
|
|
19
20
|
from fluxloop.schemas import MultiTurnSupervisorConfig
|
|
20
21
|
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
21
23
|
SupervisorDecisionType = Literal["continue", "terminate"]
|
|
22
24
|
|
|
23
25
|
|
|
@@ -118,6 +120,13 @@ class ConversationSupervisor:
|
|
|
118
120
|
)
|
|
119
121
|
|
|
120
122
|
provider = (self.config.provider or "openai").lower()
|
|
123
|
+
logger.debug(
|
|
124
|
+
"supervisor.decide provider=%s turns=%d persona=%r service=%r",
|
|
125
|
+
provider,
|
|
126
|
+
len(turns),
|
|
127
|
+
persona_description,
|
|
128
|
+
service_context,
|
|
129
|
+
)
|
|
121
130
|
if provider == "mock":
|
|
122
131
|
return self._mock_decision(conversation_state)
|
|
123
132
|
if provider == "openai":
|
|
@@ -131,6 +140,11 @@ class ConversationSupervisor:
|
|
|
131
140
|
"""Deterministic supervisor used for tests, scripted runs, or offline modes."""
|
|
132
141
|
|
|
133
142
|
metadata = self.config.metadata or {}
|
|
143
|
+
logger.debug(
|
|
144
|
+
"mock supervisor state: scripted_questions=%s context_keys=%s",
|
|
145
|
+
isinstance(metadata.get("scripted_questions"), list),
|
|
146
|
+
list(conversation_state.get("context", {}).keys()),
|
|
147
|
+
)
|
|
134
148
|
|
|
135
149
|
scripted_questions = metadata.get("scripted_questions")
|
|
136
150
|
if isinstance(scripted_questions, list):
|
|
@@ -13,6 +13,13 @@ from typing import Any, Dict, List, Optional, Tuple, Literal
|
|
|
13
13
|
|
|
14
14
|
import shutil
|
|
15
15
|
from rich.console import Console
|
|
16
|
+
from rich.progress import (
|
|
17
|
+
BarColumn,
|
|
18
|
+
Progress,
|
|
19
|
+
SpinnerColumn,
|
|
20
|
+
TextColumn,
|
|
21
|
+
TimeRemainingColumn,
|
|
22
|
+
)
|
|
16
23
|
|
|
17
24
|
from ..config import AggregateConfig, EvaluationConfig, EvaluatorConfig
|
|
18
25
|
from ..llm import LLMEvaluationManager, LLMResult
|
|
@@ -321,9 +328,26 @@ def run_evaluation(
|
|
|
321
328
|
output_dir=options.output_dir,
|
|
322
329
|
)
|
|
323
330
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
331
|
+
console.print("\n[bold green]🧪 Evaluating traces...[/bold green]\n")
|
|
332
|
+
|
|
333
|
+
with Progress(
|
|
334
|
+
SpinnerColumn(),
|
|
335
|
+
TextColumn("[progress.description]{task.description}"),
|
|
336
|
+
BarColumn(),
|
|
337
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
338
|
+
TimeRemainingColumn(),
|
|
339
|
+
TextColumn("({task.completed} of {task.total})"),
|
|
340
|
+
console=console,
|
|
341
|
+
) as progress:
|
|
342
|
+
task_id = progress.add_task(
|
|
343
|
+
"Evaluating traces",
|
|
344
|
+
total=len(traces),
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
for trace in traces:
|
|
348
|
+
outcome = _evaluate_trace(trace, config, llm_manager)
|
|
349
|
+
results.append(outcome)
|
|
350
|
+
progress.advance(task_id)
|
|
327
351
|
|
|
328
352
|
_write_per_trace(results, options.output_dir)
|
|
329
353
|
|
|
@@ -7,6 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import contextvars
|
|
9
9
|
import json
|
|
10
|
+
import logging
|
|
10
11
|
import inspect
|
|
11
12
|
import os
|
|
12
13
|
import time
|
|
@@ -27,6 +28,7 @@ from .arg_binder import ArgBinder
|
|
|
27
28
|
from .conversation_supervisor import ConversationSupervisor, SupervisorDecision
|
|
28
29
|
|
|
29
30
|
console = Console()
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
class ExperimentRunner:
|
|
@@ -499,6 +501,13 @@ class ExperimentRunner:
|
|
|
499
501
|
while True:
|
|
500
502
|
callback_messages: Dict[str, Any] = {}
|
|
501
503
|
|
|
504
|
+
logger.debug(
|
|
505
|
+
"multi-turn request: iteration=%s turn=%s input_type=%s preview=%r",
|
|
506
|
+
iteration,
|
|
507
|
+
turn_count,
|
|
508
|
+
type(current_user_input).__name__,
|
|
509
|
+
current_user_input if isinstance(current_user_input, str) else str(current_user_input),
|
|
510
|
+
)
|
|
502
511
|
result = await self._call_agent(
|
|
503
512
|
agent_func,
|
|
504
513
|
current_user_input,
|
|
@@ -524,6 +533,13 @@ class ExperimentRunner:
|
|
|
524
533
|
coerced = await self._coerce_result_to_text(result)
|
|
525
534
|
assistant_output = coerced if coerced is not None else str(result)
|
|
526
535
|
|
|
536
|
+
logger.debug(
|
|
537
|
+
"multi-turn agent output: raw_type=%s assistant_output_type=%s preview=%r",
|
|
538
|
+
type(result).__name__,
|
|
539
|
+
type(assistant_output).__name__,
|
|
540
|
+
assistant_output if isinstance(assistant_output, str) else str(assistant_output),
|
|
541
|
+
)
|
|
542
|
+
|
|
527
543
|
final_output = assistant_output
|
|
528
544
|
|
|
529
545
|
conversation_state["turns"].append(
|
|
@@ -547,6 +563,14 @@ class ExperimentRunner:
|
|
|
547
563
|
service_context=service_context,
|
|
548
564
|
)
|
|
549
565
|
last_decision = decision
|
|
566
|
+
logger.debug(
|
|
567
|
+
"multi-turn supervisor decision: decision=%s termination=%r next_type=%s",
|
|
568
|
+
decision.decision,
|
|
569
|
+
decision.termination_reason,
|
|
570
|
+
type(decision.next_user_message).__name__
|
|
571
|
+
if decision.next_user_message is not None
|
|
572
|
+
else None,
|
|
573
|
+
)
|
|
550
574
|
|
|
551
575
|
if decision.decision == "terminate":
|
|
552
576
|
termination_reason = (
|
|
@@ -569,6 +593,11 @@ class ExperimentRunner:
|
|
|
569
593
|
"Supervisor decided to continue but provided no next_user_message."
|
|
570
594
|
)
|
|
571
595
|
|
|
596
|
+
logger.debug(
|
|
597
|
+
"multi-turn next user message: type=%s preview=%r",
|
|
598
|
+
type(next_user_message).__name__,
|
|
599
|
+
next_user_message if isinstance(next_user_message, str) else str(next_user_message),
|
|
600
|
+
)
|
|
572
601
|
conversation_state["turns"].append(
|
|
573
602
|
{
|
|
574
603
|
"role": "user",
|
|
@@ -155,7 +155,7 @@ def create_simulation_config(project_name: str) -> str:
|
|
|
155
155
|
When continuing, craft the next user message consistent with the persona.
|
|
156
156
|
When terminating, explain the reason and provide any closing notes.
|
|
157
157
|
metadata:
|
|
158
|
-
scripted_questions: [] #
|
|
158
|
+
scripted_questions: [] # Array of user utterances for sequential playback (e.g., ["First question", "Second question", ...])
|
|
159
159
|
mock_decision: terminate # Fallback when no scripted questions remain
|
|
160
160
|
mock_reason: script_complete # Termination reason for scripted runs
|
|
161
161
|
mock_closing: "Thanks for the help. I have no further questions."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fluxloop-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.25
|
|
4
4
|
Summary: FluxLoop CLI for running agent simulations
|
|
5
5
|
Author-email: FluxLoop Team <team@fluxloop.dev>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -26,6 +26,7 @@ Requires-Dist: httpx>=0.24.0
|
|
|
26
26
|
Requires-Dist: rich>=13.0
|
|
27
27
|
Requires-Dist: python-dotenv>=1.0.0
|
|
28
28
|
Requires-Dist: fluxloop>=0.1.0
|
|
29
|
+
Requires-Dist: ruamel.yaml>=0.17.0
|
|
29
30
|
Provides-Extra: dev
|
|
30
31
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
32
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fluxloop-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.25"
|
|
8
8
|
description = "FluxLoop CLI for running agent simulations"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "FluxLoop Team", email = "team@fluxloop.dev"}
|
|
@@ -32,6 +32,7 @@ dependencies = [
|
|
|
32
32
|
"rich>=13.0",
|
|
33
33
|
"python-dotenv>=1.0.0",
|
|
34
34
|
"fluxloop>=0.1.0",
|
|
35
|
+
"ruamel.yaml>=0.17.0",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
[project.optional-dependencies]
|
|
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
|
{fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/reporting/__init__.py
RENAMED
|
File without changes
|
{fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/reporting/html.py
RENAMED
|
File without changes
|
{fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/engine/reporting/markdown.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/intent_recognition.py
RENAMED
|
File without changes
|
{fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/response_clarity.py
RENAMED
|
File without changes
|
{fluxloop_cli-0.2.23 → fluxloop_cli-0.2.25}/fluxloop_cli/evaluation/prompts/response_consistency.py
RENAMED
|
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
|