pymetadataeditor 0.3.0__tar.gz → 0.3.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.
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/PKG-INFO +3 -3
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pymetadataeditor/interface.py +107 -26
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pymetadataeditor/templates.py +17 -2
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pyproject.toml +3 -3
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/LICENSE +0 -0
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/README.md +0 -0
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pymetadataeditor/__init__.py +0 -0
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pymetadataeditor/llm_helpers.py +0 -0
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pymetadataeditor/requester.py +0 -0
- {pymetadataeditor-0.3.0 → pymetadataeditor-0.3.2}/pymetadataeditor/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pymetadataeditor
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Metadata Editor client for python
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Gordon Blackadder
|
|
@@ -11,8 +11,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Requires-Dist: markitdown (>=0.0.1a3,<0.0.2)
|
|
14
|
-
Requires-Dist: metadataschemas (>=0.1.
|
|
15
|
-
Requires-Dist: openai (>=
|
|
14
|
+
Requires-Dist: metadataschemas (>=0.1.3,<0.2.0)
|
|
15
|
+
Requires-Dist: openai (>=2.8.0,<3.0.0)
|
|
16
16
|
Requires-Dist: pandas (>=2.2.2,<3.0.0)
|
|
17
17
|
Requires-Dist: pydantic (>=2.7.4,<3.0.0)
|
|
18
18
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
@@ -427,12 +427,21 @@ class MetadataEditor:
|
|
|
427
427
|
else:
|
|
428
428
|
metadata_type = self._templates[template_uid]["metadata_type"]
|
|
429
429
|
if "class" not in self._templates[template_uid] or apply_template_rules is False:
|
|
430
|
-
|
|
430
|
+
try:
|
|
431
|
+
parent_schema = self._mm.metadata_class_from_name(metadata_type)
|
|
432
|
+
except ValueError:
|
|
433
|
+
warnings.warn(
|
|
434
|
+
f"Could not find a metadata class for template type '{metadata_type}', UID '{template_uid}'. "
|
|
435
|
+
f"Building a class from the template without a parent schema."
|
|
436
|
+
)
|
|
437
|
+
parent_schema = SchemaBaseModel
|
|
438
|
+
|
|
431
439
|
klass = pydantic_from_template(
|
|
432
440
|
temp.template,
|
|
433
441
|
parent_schema=parent_schema,
|
|
434
442
|
uid=template_uid,
|
|
435
443
|
name=temp.name,
|
|
444
|
+
metadata_type=metadata_type,
|
|
436
445
|
apply_rules=apply_template_rules,
|
|
437
446
|
)
|
|
438
447
|
if apply_template_rules:
|
|
@@ -693,7 +702,7 @@ class MetadataEditor:
|
|
|
693
702
|
tokenizer_model="o200k_base",
|
|
694
703
|
max_tokens=128_000,
|
|
695
704
|
llm_base_url: Optional[str] = None,
|
|
696
|
-
|
|
705
|
+
azure_deployment_name: Optional[str] = None,
|
|
697
706
|
) -> Union[BaseModel, Dict, str]:
|
|
698
707
|
"""Automatically generate *draft* metadata for a project based on local files or web pages.
|
|
699
708
|
|
|
@@ -730,11 +739,13 @@ class MetadataEditor:
|
|
|
730
739
|
max_tokens (int): The maximum number of tokens to use when sending the content to OpenAI.
|
|
731
740
|
Defaults to 128_000, which has been the typical maximum for the 4o model.
|
|
732
741
|
llm_base_url (Optional[str]): The base URL for the LLM API. If None, the default URL is used which
|
|
733
|
-
sends the request to OpenAI.
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
742
|
+
sends the request to OpenAI. Alternatively, this can be the base URL for a local LLM instance such as
|
|
743
|
+
Ollama or a private deployment of an LLM model. If using Azure OpenAI, this should be the Azure
|
|
744
|
+
endpoint URL (eg "https://my-azure-openai-resource.openai.azure.com/") and the azure_deployment_name
|
|
745
|
+
parameter should also be set.
|
|
746
|
+
azure_deployment_name (Optional[str]): Used when an organization has its own deployment of an LLM model in
|
|
747
|
+
Azure, possibly for privacy reasons. Be sure to provide the llm_base_url parameter as well which should
|
|
748
|
+
be the Azure endpoint URL.
|
|
738
749
|
|
|
739
750
|
Returns:
|
|
740
751
|
(Union[BaseModel, Dict, str]): If mode == 'dict', a dictionary is returned. If mode == 'pydantic', a
|
|
@@ -776,7 +787,8 @@ class MetadataEditor:
|
|
|
776
787
|
metadata_producer_organization="My Organization",
|
|
777
788
|
filename="output.xlsx",
|
|
778
789
|
title="My Metadata",
|
|
779
|
-
|
|
790
|
+
llm_base_url="https://my-azure-openai-resource.openai.azure.com/",
|
|
791
|
+
azure_deployment_name="my-llm-deployment"
|
|
780
792
|
)
|
|
781
793
|
```
|
|
782
794
|
"""
|
|
@@ -786,10 +798,15 @@ class MetadataEditor:
|
|
|
786
798
|
metadata_type_or_template_uid, apply_template_rules=False
|
|
787
799
|
)
|
|
788
800
|
enc = tiktoken.get_encoding(tokenizer_model)
|
|
789
|
-
if
|
|
801
|
+
if azure_deployment_name is None:
|
|
790
802
|
client = OpenAI(api_key=llm_api_key, base_url=llm_base_url)
|
|
791
803
|
else:
|
|
792
|
-
client = AzureOpenAI(
|
|
804
|
+
client = AzureOpenAI(
|
|
805
|
+
api_key=llm_api_key,
|
|
806
|
+
azure_endpoint=llm_base_url,
|
|
807
|
+
azure_deployment=azure_deployment_name,
|
|
808
|
+
api_version="2024-10-01",
|
|
809
|
+
)
|
|
793
810
|
|
|
794
811
|
system_prompt = f"You are an expert on producing {metadata_type} documentation. "
|
|
795
812
|
system_prompt += (
|
|
@@ -829,9 +846,7 @@ class MetadataEditor:
|
|
|
829
846
|
print(f"Reading {doc}, running token count is {num_tokens}")
|
|
830
847
|
messages += user_message
|
|
831
848
|
|
|
832
|
-
endpoint_name =
|
|
833
|
-
azure_llm_base_url if azure_llm_base_url is not None else "OpenAI" if llm_base_url is None else llm_base_url
|
|
834
|
-
)
|
|
849
|
+
endpoint_name = llm_base_url if llm_base_url is not None else "OpenAI"
|
|
835
850
|
print(f"Sending to {endpoint_name}, this may take a few minutes...")
|
|
836
851
|
try:
|
|
837
852
|
completion = client.beta.chat.completions.parse(
|
|
@@ -876,7 +891,7 @@ class MetadataEditor:
|
|
|
876
891
|
tokenizer_model="o200k_base",
|
|
877
892
|
max_tokens=128_000,
|
|
878
893
|
llm_base_url: Optional[str] = None,
|
|
879
|
-
|
|
894
|
+
azure_deployment_name: Optional[str] = None,
|
|
880
895
|
) -> Union[BaseModel, Dict, str]:
|
|
881
896
|
"""Augment existing metadata with information from files or web pages.
|
|
882
897
|
|
|
@@ -905,11 +920,13 @@ class MetadataEditor:
|
|
|
905
920
|
max_tokens (int): The maximum number of tokens to use when sending the content to OpenAI.
|
|
906
921
|
Defaults to 128_000, which has been the typical maximum for the 4o model.
|
|
907
922
|
llm_base_url (Optional[str]): The base URL for the LLM API. If None, the default URL is used which
|
|
908
|
-
sends the request to OpenAI.
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
923
|
+
sends the request to OpenAI. Alternatively, this can be the base URL for a local LLM instance such as
|
|
924
|
+
Ollama or a private deployment of an LLM model. If using Azure OpenAI, this should be the Azure
|
|
925
|
+
endpoint URL (eg "https://my-azure-openai-resource.openai.azure.com/") and the azure_deployment_name
|
|
926
|
+
parameter should also be set.
|
|
927
|
+
azure_deployment_name (Optional[str]): Used when an organization has its own deployment of an LLM model in
|
|
928
|
+
Azure, possibly for privacy reasons. Be sure to provide the llm_base_url parameter as well which should
|
|
929
|
+
be the Azure endpoint URL.
|
|
913
930
|
|
|
914
931
|
Returns:
|
|
915
932
|
Union[BaseModel, Dict, str]: The augmented metadata.
|
|
@@ -936,7 +953,8 @@ class MetadataEditor:
|
|
|
936
953
|
output_mode="pydantic",
|
|
937
954
|
metadata_producer_organization="My Organization",
|
|
938
955
|
prefix="<AI>",
|
|
939
|
-
|
|
956
|
+
llm_base_url="https://my-azure-openai-resource.openai.azure.com/",
|
|
957
|
+
azure_deployment_name="my-llm-deployment"
|
|
940
958
|
)
|
|
941
959
|
```
|
|
942
960
|
"""
|
|
@@ -1008,7 +1026,7 @@ class MetadataEditor:
|
|
|
1008
1026
|
tokenizer_model=tokenizer_model,
|
|
1009
1027
|
max_tokens=max_tokens,
|
|
1010
1028
|
llm_base_url=llm_base_url,
|
|
1011
|
-
|
|
1029
|
+
azure_deployment_name=azure_deployment_name,
|
|
1012
1030
|
)
|
|
1013
1031
|
|
|
1014
1032
|
if new_metadata is None or len(new_metadata) == 0:
|
|
@@ -1043,7 +1061,7 @@ class MetadataEditor:
|
|
|
1043
1061
|
document, geospatial, image, indicator, indicators_db, microdata, resource, script, table, video
|
|
1044
1062
|
In this case we will use the default template for that metadata type.
|
|
1045
1063
|
Alternatively you can pass in the UID of a template. This is required if the metadata is a dictionary
|
|
1046
|
-
otherwise
|
|
1064
|
+
otherwise the UID associated with the pydantic model or the Excel file will be used.
|
|
1047
1065
|
|
|
1048
1066
|
Returns:
|
|
1049
1067
|
int: The ID of the newly created document metadata
|
|
@@ -1063,6 +1081,37 @@ class MetadataEditor:
|
|
|
1063
1081
|
)
|
|
1064
1082
|
return ret["id"]
|
|
1065
1083
|
|
|
1084
|
+
# def log_project_admin_metadata(
|
|
1085
|
+
# self, id: int, metadata: Union[BaseModel, Dict, str], metadata_type_or_template_uid: Optional[str] = None
|
|
1086
|
+
# ) -> int:
|
|
1087
|
+
# """Validates and logs admin metadata which can be a dictionary, a pydantic model or a path to an Excel file.
|
|
1088
|
+
|
|
1089
|
+
# Args:
|
|
1090
|
+
# id (int): The ID of the project to associate the metadata with.
|
|
1091
|
+
# metadata (dictionary or BaseModel or str): If str, it's assumed this is a path to an appropriately
|
|
1092
|
+
# formatted Excel file.
|
|
1093
|
+
# admin_metadata_template_uid (str, optional): The UID of the admin metadata template to use.
|
|
1094
|
+
# otherwise the UID associated with the pydantic model or the Excel file will be used.
|
|
1095
|
+
|
|
1096
|
+
# Returns:
|
|
1097
|
+
# int: The ID of the newly created admin metadata
|
|
1098
|
+
# """
|
|
1099
|
+
# metadata, metadata_type, uid = self._process_metadata_input(metadata, metadata_type_or_template_uid)
|
|
1100
|
+
# post_request_pth = "admin-metadata/data/"
|
|
1101
|
+
# post_json = {
|
|
1102
|
+
# "project_id": id,
|
|
1103
|
+
# "template_uid": uid,
|
|
1104
|
+
# "metadata": remove_empty_from_dict(metadata.model_dump(mode="json",
|
|
1105
|
+
# exclude_none=True,
|
|
1106
|
+
# exclude_unset=True)),
|
|
1107
|
+
# }
|
|
1108
|
+
# ret = self._apinterface.post_request(
|
|
1109
|
+
# pth=post_request_pth,
|
|
1110
|
+
# json=post_json,
|
|
1111
|
+
# id=id,
|
|
1112
|
+
# )
|
|
1113
|
+
# return ret
|
|
1114
|
+
|
|
1066
1115
|
def update_project_log_by_id(self, id: int, new_metadata: Union[BaseModel, Dict, str]):
|
|
1067
1116
|
"""Updates the record of the metadata.
|
|
1068
1117
|
|
|
@@ -1252,7 +1301,6 @@ class MetadataEditor:
|
|
|
1252
1301
|
new_index = templates.index.astype(int)
|
|
1253
1302
|
except ValueError:
|
|
1254
1303
|
pass
|
|
1255
|
-
else:
|
|
1256
1304
|
templates.index = new_index
|
|
1257
1305
|
return templates
|
|
1258
1306
|
|
|
@@ -1272,10 +1320,23 @@ class MetadataEditor:
|
|
|
1272
1320
|
raise KeyError(f"No result returned.\nResponse: {response}")
|
|
1273
1321
|
|
|
1274
1322
|
temp = pd.Series(response["result"], name=response["result"]["name"])
|
|
1275
|
-
self._templates[uid] = {"template": temp, "metadata_type": self._mm.standardize_metadata_name(temp.data_type)}
|
|
1276
1323
|
try:
|
|
1277
|
-
|
|
1278
|
-
|
|
1324
|
+
standard_type_name = self._mm.standardize_metadata_name(temp.data_type)
|
|
1325
|
+
except ValueError:
|
|
1326
|
+
standard_type_name = temp.data_type
|
|
1327
|
+
self._templates[uid] = {"template": temp, "metadata_type": standard_type_name}
|
|
1328
|
+
try:
|
|
1329
|
+
try:
|
|
1330
|
+
parent_schema = self._mm.metadata_class_from_name(temp.data_type)
|
|
1331
|
+
except ValueError:
|
|
1332
|
+
warnings.warn(
|
|
1333
|
+
f"Could not find a metadata class for template type '{standard_type_name}', UID '{uid}'. "
|
|
1334
|
+
f"Building a class from the template without a parent schema."
|
|
1335
|
+
)
|
|
1336
|
+
parent_schema = SchemaBaseModel
|
|
1337
|
+
klass = pydantic_from_template(
|
|
1338
|
+
temp.template, parent_schema=parent_schema, uid=uid, name=temp.name, metadata_type=standard_type_name
|
|
1339
|
+
)
|
|
1279
1340
|
self._templates[uid]["class"] = klass
|
|
1280
1341
|
finally:
|
|
1281
1342
|
return temp
|
|
@@ -1474,6 +1535,26 @@ class MetadataEditor:
|
|
|
1474
1535
|
metadata["description"] = description
|
|
1475
1536
|
self._apinterface.post_request("collections/update/{}", id=id, json=metadata)
|
|
1476
1537
|
|
|
1538
|
+
def copy_collection(self, source_id: int, target_id: int):
|
|
1539
|
+
"""Copy projects and users from one collection to another.
|
|
1540
|
+
|
|
1541
|
+
Args:
|
|
1542
|
+
source_id (int): The ID of the source collection.
|
|
1543
|
+
target_id (int): The ID of the target collection.
|
|
1544
|
+
|
|
1545
|
+
"""
|
|
1546
|
+
self._apinterface.post_request("collections/copy/", json={"source_id": source_id, "target_id": target_id})
|
|
1547
|
+
|
|
1548
|
+
def move_collection(self, source_id, target_id):
|
|
1549
|
+
"""Move source collection to be a sub-collection of the target collection.
|
|
1550
|
+
|
|
1551
|
+
Args:
|
|
1552
|
+
source_id (int): The ID of the source collection.
|
|
1553
|
+
target_id (int): The ID of the target collection.
|
|
1554
|
+
|
|
1555
|
+
"""
|
|
1556
|
+
self._apinterface.post_request("collections/move/", json={"source_id": source_id, "target_id": target_id})
|
|
1557
|
+
|
|
1477
1558
|
def count_projects_in_collection(self, collection: int) -> int:
|
|
1478
1559
|
"""Count the number of projects you have access to.
|
|
1479
1560
|
|
|
@@ -617,7 +617,12 @@ def append_variables_and_data_files(model_elements, parent_schema, apply_rules:
|
|
|
617
617
|
|
|
618
618
|
|
|
619
619
|
def pydantic_from_template(
|
|
620
|
-
template: Dict,
|
|
620
|
+
template: Dict,
|
|
621
|
+
parent_schema: Type[SchemaBaseModel],
|
|
622
|
+
uid: str,
|
|
623
|
+
name: Optional[str] = None,
|
|
624
|
+
metadata_type: Optional[str] = None,
|
|
625
|
+
apply_rules: bool = True,
|
|
621
626
|
) -> Type[BaseModel]:
|
|
622
627
|
"""Generate a Pydantic model from a given template.
|
|
623
628
|
|
|
@@ -626,6 +631,8 @@ def pydantic_from_template(
|
|
|
626
631
|
parent_schema (Type[SchemaBaseModel]): The parent schema model to base the new model on.
|
|
627
632
|
uid (str): A unique identifier for the new model.
|
|
628
633
|
name (Optional[str], optional): The name of the new model. Defaults to None.
|
|
634
|
+
metadata_type (Optional[str], optional): The metadata type for the new model used if the parent_schema is
|
|
635
|
+
SchemaBaseModel. Otherwise the metadata_type of the parent_schema is used. Defaults to None.
|
|
629
636
|
apply_rules (bool, optional): Whether to apply rules to the model elements. Defaults to True.
|
|
630
637
|
|
|
631
638
|
Returns:
|
|
@@ -659,4 +666,12 @@ def pydantic_from_template(
|
|
|
659
666
|
hasattr(parent_schema._metadata_type__, "default") and parent_schema._metadata_type__.default == "microdata"
|
|
660
667
|
):
|
|
661
668
|
model_elements = append_variables_and_data_files(model_elements, parent_schema, apply_rules=apply_rules)
|
|
662
|
-
|
|
669
|
+
new_model = create_model_for_template(model_elements, parent_schema, name, uid=uid)
|
|
670
|
+
if parent_schema is SchemaBaseModel:
|
|
671
|
+
if metadata_type is None:
|
|
672
|
+
raise ValueError("metadata_type must be provided if parent_schema is SchemaBaseModel. ")
|
|
673
|
+
new_model._template_name__ = f"{name.replace('-', '_').replace(' ', '_')}"
|
|
674
|
+
new_model._template_uid__ = uid
|
|
675
|
+
new_model._metadata_type__ = metadata_type.replace("-", "_").replace(" ", "_")
|
|
676
|
+
new_model._metadata_type_version__ = "unknown"
|
|
677
|
+
return new_model
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pymetadataeditor"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Metadata Editor client for python"
|
|
5
5
|
authors = ["Gordon Blackadder <gblackadder@worldbank.org>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -12,10 +12,10 @@ requests = "^2.32.3"
|
|
|
12
12
|
urllib3 = "^2.2.2" # earlier versions have known security issues
|
|
13
13
|
pandas = "^2.2.2"
|
|
14
14
|
pydantic = "^2.7.4"
|
|
15
|
-
openai = "^
|
|
15
|
+
openai = "^2.8.0"
|
|
16
16
|
tiktoken = "^0.8.0"
|
|
17
17
|
markitdown = "^0.0.1a3"
|
|
18
|
-
metadataschemas = "^0.1.
|
|
18
|
+
metadataschemas = "^0.1.3"
|
|
19
19
|
|
|
20
20
|
[tool.poetry.group.dev.dependencies]
|
|
21
21
|
pytest = "^8.2.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|