nuclia 4.9.1__tar.gz → 4.9.2__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.
Files changed (96) hide show
  1. {nuclia-4.9.1 → nuclia-4.9.2}/.gitignore +1 -0
  2. {nuclia-4.9.1 → nuclia-4.9.2}/CHANGELOG.md +6 -0
  3. {nuclia-4.9.1 → nuclia-4.9.2}/PKG-INFO +1 -1
  4. nuclia-4.9.2/VERSION +1 -0
  5. {nuclia-4.9.1 → nuclia-4.9.2}/docs/04-upload.md +40 -0
  6. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/kb.py +65 -0
  7. nuclia-4.9.2/nuclia/sdk/extract_strategy.py +98 -0
  8. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/kb.py +6 -0
  9. nuclia-4.9.2/nuclia/tests/test_kb/test_extract_strategies.py +16 -0
  10. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia.egg-info/PKG-INFO +1 -1
  11. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia.egg-info/SOURCES.txt +2 -0
  12. nuclia-4.9.1/VERSION +0 -1
  13. {nuclia-4.9.1 → nuclia-4.9.2}/LICENSE +0 -0
  14. {nuclia-4.9.1 → nuclia-4.9.2}/MANIFEST.in +0 -0
  15. {nuclia-4.9.1 → nuclia-4.9.2}/Makefile +0 -0
  16. {nuclia-4.9.1 → nuclia-4.9.2}/README.md +0 -0
  17. {nuclia-4.9.1 → nuclia-4.9.2}/docs/01-README.md +0 -0
  18. {nuclia-4.9.1 → nuclia-4.9.2}/docs/02-auth.md +0 -0
  19. {nuclia-4.9.1 → nuclia-4.9.2}/docs/03-kb.md +0 -0
  20. {nuclia-4.9.1 → nuclia-4.9.2}/docs/05-search.md +0 -0
  21. {nuclia-4.9.1 → nuclia-4.9.2}/docs/06-read.md +0 -0
  22. {nuclia-4.9.1 → nuclia-4.9.2}/docs/07-nua.md +0 -0
  23. {nuclia-4.9.1 → nuclia-4.9.2}/docs/08-import-export.md +0 -0
  24. {nuclia-4.9.1 → nuclia-4.9.2}/docs/09-kb-backup.md +0 -0
  25. {nuclia-4.9.1 → nuclia-4.9.2}/docs/10-manage.md +0 -0
  26. {nuclia-4.9.1 → nuclia-4.9.2}/docs/11-activity-log.md +0 -0
  27. {nuclia-4.9.1 → nuclia-4.9.2}/docs/12-da-agents.md +0 -0
  28. {nuclia-4.9.1 → nuclia-4.9.2}/docs/13-ai-agents.md +0 -0
  29. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/__init__.py +0 -0
  30. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/cli/__init__.py +0 -0
  31. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/cli/run.py +0 -0
  32. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/cli/utils.py +0 -0
  33. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/config.py +0 -0
  34. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/data.py +0 -0
  35. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/decorators.py +0 -0
  36. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/exceptions.py +0 -0
  37. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/__init__.py +0 -0
  38. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/conversations.py +0 -0
  39. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/models.py +0 -0
  40. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/nua.py +0 -0
  41. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/nua_chat.py +0 -0
  42. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/nua_responses.py +0 -0
  43. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/lib/utils.py +0 -0
  44. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/py.typed +0 -0
  45. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/__init__.py +0 -0
  46. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/accounts.py +0 -0
  47. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/agent.py +0 -0
  48. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/auth.py +0 -0
  49. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/backup.py +0 -0
  50. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/export_import.py +0 -0
  51. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/kbs.py +0 -0
  52. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/logger.py +0 -0
  53. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/logs.py +0 -0
  54. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/nua.py +0 -0
  55. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/nucliadb.py +0 -0
  56. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/predict.py +0 -0
  57. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/process.py +0 -0
  58. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/remi.py +0 -0
  59. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/resource.py +0 -0
  60. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/search.py +0 -0
  61. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/task.py +0 -0
  62. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/upload.py +0 -0
  63. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/sdk/zones.py +0 -0
  64. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/__init__.py +0 -0
  65. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/assets/conversation.json +0 -0
  66. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/conftest.py +0 -0
  67. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/fixtures.py +0 -0
  68. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_backup.py +0 -0
  69. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_conversation.py +0 -0
  70. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_export_import.py +0 -0
  71. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_graph.py +0 -0
  72. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_labels.py +0 -0
  73. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_logs.py +0 -0
  74. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_remi.py +0 -0
  75. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_resource.py +0 -0
  76. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_search.py +0 -0
  77. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_kb/test_tasks.py +0 -0
  78. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_manage/__init__.py +0 -0
  79. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_manage/test_account.py +0 -0
  80. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_manage/test_auth.py +0 -0
  81. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_manage/test_kb.py +0 -0
  82. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_nua/__init__.py +0 -0
  83. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_nua/test_agent.py +0 -0
  84. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_nua/test_predict.py +0 -0
  85. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_nucliadb/__init__.py +0 -0
  86. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/test_nucliadb/test_crud.py +0 -0
  87. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/unit/__init__.py +0 -0
  88. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/unit/test_export_import.py +0 -0
  89. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia/tests/unit/test_nua_responses.py +0 -0
  90. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia.egg-info/dependency_links.txt +0 -0
  91. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia.egg-info/entry_points.txt +0 -0
  92. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia.egg-info/requires.txt +0 -0
  93. {nuclia-4.9.1 → nuclia-4.9.2}/nuclia.egg-info/top_level.txt +0 -0
  94. {nuclia-4.9.1 → nuclia-4.9.2}/pyproject.toml +0 -0
  95. {nuclia-4.9.1 → nuclia-4.9.2}/setup.cfg +0 -0
  96. {nuclia-4.9.1 → nuclia-4.9.2}/uv.lock +0 -0
@@ -4,6 +4,7 @@ __pycache__
4
4
  build
5
5
  dist
6
6
  tmp
7
+ _tmp
7
8
  data
8
9
  venv
9
10
  .venv
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## 4.9.2 (2025-06-12)
5
+
6
+
7
+ - Support extract strategies
8
+
9
+
4
10
  ## 4.9.1 (2025-06-10)
5
11
 
6
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nuclia
3
- Version: 4.9.1
3
+ Version: 4.9.2
4
4
  Summary: Nuclia Python SDK
5
5
  Author-email: Nuclia <info@nuclia.com>
6
6
  License-Expression: MIT
nuclia-4.9.2/VERSION ADDED
@@ -0,0 +1 @@
1
+ 4.9.2
@@ -250,3 +250,43 @@ kb.update_graph(slug="graph1", overrides=True, graph=[
250
250
  }
251
251
  ])
252
252
  ```
253
+
254
+ ## Use extract strategies
255
+
256
+ Extract strategies allow you to perform specific processing at ingestion time. It can be useful to handle complex tables extraction, or to support unusual layouts.
257
+
258
+ ### Manage extract strategies
259
+
260
+ CLI:
261
+
262
+ ```bash
263
+ nuclia kb extract_strategies add --config='{"name":"strategy1","vllm_config":{}}'
264
+ nuclia kb extract_strategies list
265
+ nuclia kb extract_strategies delete --id=1361c0c7-918a-4a7f-b44b-ba37437619fb
266
+ ```
267
+
268
+ SDK:
269
+
270
+ ```python
271
+ from nuclia import sdk
272
+ extract_strategies = sdk.NucliaKB().extract_strategies
273
+ print(extract_strategies.list())
274
+ id = extract_strategies.add(config={"name": "strategy1", "vllm_config": {}})
275
+ extract_strategies.delete(id=id)
276
+ ```
277
+
278
+ ### Use extract strategies
279
+
280
+ CLI:
281
+
282
+ ```bash
283
+ nuclia kb upload file --path=FILE_PATH --extract_strategy=1361c0c7-918a-4a7f-b44b-ba37437619fb
284
+ ```
285
+
286
+ SDK:
287
+
288
+ ```python
289
+ from nuclia import sdk
290
+ upload = sdk.NucliaUpload()
291
+ upload.file(path=FILE_PATH, extract_strategy="1361c0c7-918a-4a7f-b44b-ba37437619fb")
292
+ ```
@@ -23,6 +23,7 @@ from nuclia_models.events.activity_logs import ( # type: ignore
23
23
  )
24
24
  from nuclia_models.events.remi import RemiQuery
25
25
  from nuclia_models.worker.tasks import TaskStartKB
26
+ from nuclia_models.config.proto import ExtractConfig
26
27
  from nuclia.exceptions import RateLimitError
27
28
  from nuclia.lib.utils import handle_http_sync_errors, handle_http_async_errors
28
29
  from datetime import datetime
@@ -58,6 +59,8 @@ STOP_TASK = "/task/{task_id}/stop"
58
59
  DELETE_TASK = "/task/{task_id}"
59
60
  GET_TASK = "/task/{task_id}/inspect"
60
61
  RESTART_TASK = "/task/{task_id}/restart"
62
+ EXTRACT_STRATEGIES = "/extract_strategies"
63
+ DELETE_EXTRACT_STRATEGY = "/extract_strategies/strategy/{id}"
61
64
 
62
65
  DOWNLOAD_FORMAT_HEADERS = {
63
66
  DownloadFormat.CSV: "text/csv",
@@ -487,6 +490,37 @@ class NucliaDBClient(BaseNucliaDBClient):
487
490
  handle_http_sync_errors(response)
488
491
  return response
489
492
 
493
+ def list_extract_strategies(self) -> httpx.Response:
494
+ if self.reader_session is None:
495
+ raise Exception("KB not configured")
496
+
497
+ response: httpx.Response = self.reader_session.get(
498
+ f"{self.url}{EXTRACT_STRATEGIES}"
499
+ )
500
+ handle_http_sync_errors(response)
501
+ return response
502
+
503
+ def add_extract_strategy(self, config: ExtractConfig) -> httpx.Response:
504
+ if self.writer_session is None:
505
+ raise Exception("KB not configured")
506
+
507
+ response: httpx.Response = self.writer_session.post(
508
+ f"{self.url}{EXTRACT_STRATEGIES}",
509
+ json=config.model_dump(mode="json", exclude_unset=True),
510
+ )
511
+ handle_http_sync_errors(response)
512
+ return response
513
+
514
+ def delete_extract_strategy(self, strategy_id: str) -> httpx.Response:
515
+ if self.writer_session is None:
516
+ raise Exception("KB not configured")
517
+
518
+ response: httpx.Response = self.writer_session.delete(
519
+ f"{self.url}{DELETE_EXTRACT_STRATEGY.format(id=strategy_id)}",
520
+ )
521
+ handle_http_sync_errors(response)
522
+ return response
523
+
490
524
 
491
525
  class AsyncNucliaDBClient(BaseNucliaDBClient):
492
526
  reader_session: Optional[httpx.AsyncClient] = None
@@ -813,3 +847,34 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
813
847
  )
814
848
  await handle_http_async_errors(response)
815
849
  return response
850
+
851
+ async def list_extract_strategies(self) -> httpx.Response:
852
+ if self.reader_session is None:
853
+ raise Exception("KB not configured")
854
+
855
+ response: httpx.Response = await self.reader_session.get(
856
+ f"{self.url}{EXTRACT_STRATEGIES}"
857
+ )
858
+ await handle_http_async_errors(response)
859
+ return response
860
+
861
+ async def add_extract_strategy(self, config: ExtractConfig) -> httpx.Response:
862
+ if self.writer_session is None:
863
+ raise Exception("KB not configured")
864
+
865
+ response: httpx.Response = await self.writer_session.post(
866
+ f"{self.url}{EXTRACT_STRATEGIES}",
867
+ json=config.model_dump(mode="json", exclude_unset=True),
868
+ )
869
+ await handle_http_async_errors(response)
870
+ return response
871
+
872
+ async def delete_extract_strategy(self, strategy_id: str) -> httpx.Response:
873
+ if self.writer_session is None:
874
+ raise Exception("KB not configured")
875
+
876
+ response: httpx.Response = await self.writer_session.delete(
877
+ f"{self.url}{DELETE_EXTRACT_STRATEGY.format(id=strategy_id)}",
878
+ )
879
+ await handle_http_async_errors(response)
880
+ return response
@@ -0,0 +1,98 @@
1
+ from nuclia.data import get_auth, get_async_auth
2
+ from nuclia.decorators import kb, pretty
3
+ from nuclia.lib.kb import NucliaDBClient, AsyncNucliaDBClient
4
+ from nuclia.sdk.auth import NucliaAuth, AsyncNucliaAuth
5
+ from nuclia_models.config.proto import ExtractConfig
6
+ from typing import Dict
7
+
8
+
9
+ class NucliaExtractStrategy:
10
+ @property
11
+ def _auth(self) -> NucliaAuth:
12
+ auth = get_auth()
13
+ return auth
14
+
15
+ @kb
16
+ @pretty
17
+ def list(self, *args, **kwargs) -> Dict[str, ExtractConfig]:
18
+ """
19
+ List extract strategies
20
+ """
21
+ ndb: NucliaDBClient = kwargs["ndb"]
22
+ response = ndb.list_extract_strategies()
23
+ return response.json()
24
+
25
+ @kb
26
+ def add(
27
+ self,
28
+ *args,
29
+ config: ExtractConfig,
30
+ **kwargs,
31
+ ) -> str:
32
+ """
33
+ Add extract strategy
34
+
35
+ :param config: strategy configuration
36
+ """
37
+ if isinstance(config, dict):
38
+ config = ExtractConfig.model_validate(config)
39
+
40
+ ndb: NucliaDBClient = kwargs["ndb"]
41
+ response = ndb.add_extract_strategy(config=config)
42
+ return response.json()
43
+
44
+ @kb
45
+ def delete(self, *args, id: str, **kwargs):
46
+ """
47
+ Delete extract strategy
48
+
49
+ :param id: ID of the strategy to delete
50
+ """
51
+ ndb: NucliaDBClient = kwargs["ndb"]
52
+ ndb.delete_extract_strategy(strategy_id=id)
53
+
54
+
55
+ class AsyncNucliaExtractStrategy:
56
+ @property
57
+ def _auth(self) -> AsyncNucliaAuth:
58
+ auth = get_async_auth()
59
+ return auth
60
+
61
+ @kb
62
+ @pretty
63
+ async def list(self, *args, **kwargs) -> Dict[str, ExtractConfig]:
64
+ """
65
+ List extract strategies
66
+ """
67
+ ndb: AsyncNucliaDBClient = kwargs["ndb"]
68
+ response = await ndb.list_extract_strategies()
69
+ return response.json()
70
+
71
+ @kb
72
+ async def add(
73
+ self,
74
+ *args,
75
+ config: ExtractConfig,
76
+ **kwargs,
77
+ ) -> str:
78
+ """
79
+ Add extract strategy
80
+
81
+ :param config: strategy configuration
82
+ """
83
+ if isinstance(config, dict):
84
+ config = ExtractConfig.model_validate(config)
85
+
86
+ ndb: AsyncNucliaDBClient = kwargs["ndb"]
87
+ response = await ndb.add_extract_strategy(config=config)
88
+ return response.json()
89
+
90
+ @kb
91
+ async def delete(self, *args, id: str, **kwargs):
92
+ """
93
+ Delete extract strategy
94
+
95
+ :param id: ID of the strategy to delete
96
+ """
97
+ ndb: AsyncNucliaDBClient = kwargs["ndb"]
98
+ await ndb.delete_extract_strategy(strategy_id=id)
@@ -30,6 +30,10 @@ from nuclia.sdk.remi import NucliaRemi, AsyncNucliaRemi
30
30
  from nuclia.sdk.resource import AsyncNucliaResource, NucliaResource
31
31
  from nuclia.sdk.search import AsyncNucliaSearch, NucliaSearch
32
32
  from nuclia.sdk.task import NucliaTask, AsyncNucliaTask
33
+ from nuclia.sdk.extract_strategy import (
34
+ NucliaExtractStrategy,
35
+ AsyncNucliaExtractStrategy,
36
+ )
33
37
  from nuclia.sdk.upload import AsyncNucliaUpload, NucliaUpload
34
38
 
35
39
 
@@ -48,6 +52,7 @@ class NucliaKB:
48
52
  self.logs = NucliaLogs()
49
53
  self.task = NucliaTask()
50
54
  self.remi = NucliaRemi()
55
+ self.extract_strategies = NucliaExtractStrategy()
51
56
 
52
57
  @kb
53
58
  def list(
@@ -459,6 +464,7 @@ class AsyncNucliaKB:
459
464
  self.logs = AsyncNucliaLogs()
460
465
  self.task = AsyncNucliaTask()
461
466
  self.remi = AsyncNucliaRemi()
467
+ self.extract_strategies = AsyncNucliaExtractStrategy()
462
468
 
463
469
  @kb
464
470
  async def list(self, **kwargs) -> ResourceList:
@@ -0,0 +1,16 @@
1
+ from nuclia.sdk.kb import NucliaKB
2
+
3
+
4
+ def test_extract_strategies(testing_config):
5
+ nkb = NucliaKB()
6
+ # preventive clean up
7
+ for id in nkb.extract_strategies.list().keys():
8
+ nkb.extract_strategies.delete(id=id)
9
+
10
+ # tests
11
+ nkb.extract_strategies.add(config={"name": "strategy1", "vllm_config": {}})
12
+ all = nkb.extract_strategies.list()
13
+ assert len(all.keys()) == 1
14
+ nkb.extract_strategies.delete(id=list(all.keys())[0])
15
+ all = nkb.extract_strategies.list()
16
+ assert len(all.keys()) == 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nuclia
3
- Version: 4.9.1
3
+ Version: 4.9.2
4
4
  Summary: Nuclia Python SDK
5
5
  Author-email: Nuclia <info@nuclia.com>
6
6
  License-Expression: MIT
@@ -49,6 +49,7 @@ nuclia/sdk/agent.py
49
49
  nuclia/sdk/auth.py
50
50
  nuclia/sdk/backup.py
51
51
  nuclia/sdk/export_import.py
52
+ nuclia/sdk/extract_strategy.py
52
53
  nuclia/sdk/kb.py
53
54
  nuclia/sdk/kbs.py
54
55
  nuclia/sdk/logger.py
@@ -70,6 +71,7 @@ nuclia/tests/assets/conversation.json
70
71
  nuclia/tests/test_kb/test_backup.py
71
72
  nuclia/tests/test_kb/test_conversation.py
72
73
  nuclia/tests/test_kb/test_export_import.py
74
+ nuclia/tests/test_kb/test_extract_strategies.py
73
75
  nuclia/tests/test_kb/test_graph.py
74
76
  nuclia/tests/test_kb/test_labels.py
75
77
  nuclia/tests/test_kb/test_logs.py
nuclia-4.9.1/VERSION DELETED
@@ -1 +0,0 @@
1
- 4.9.1
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
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