kodexa 7.0.1a8003211616__py3-none-any.whl → 7.0.1a9196667375__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.
kodexa/platform/client.py CHANGED
@@ -85,7 +85,7 @@ from kodexa.model.objects import (
85
85
  ReprocessRequest,
86
86
  PageExtensionPack,
87
87
  PageOrganization,
88
- DocumentFamilyStatistics, MessageContext, PagePrompt, Prompt, GuidanceSet, PageGuidanceSet,
88
+ DocumentFamilyStatistics, MessageContext, PagePrompt, Prompt, GuidanceSet, PageGuidanceSet, DocumentEmbedding,
89
89
  )
90
90
 
91
91
  logger = logging.getLogger()
@@ -491,9 +491,9 @@ class ComponentEndpoint(ClientEndpoint, OrganizationOwned):
491
491
  # Yield each endpoint in the current page
492
492
  for endpoint in (
493
493
  self.get_page_class(list_response.json())
494
- .model_validate(list_response.json())
495
- .set_client(self.client)
496
- .to_endpoints()
494
+ .model_validate(list_response.json())
495
+ .set_client(self.client)
496
+ .to_endpoints()
497
497
  ):
498
498
  yield endpoint
499
499
 
@@ -548,7 +548,7 @@ class ComponentEndpoint(ClientEndpoint, OrganizationOwned):
548
548
  Returns:
549
549
  The created component.
550
550
  """
551
- url = f"/api/{self.get_type()}/{self.organization.slug}/"
551
+ url = f"/api/{self.get_type()}/{self.organization.slug}"
552
552
  get_response = self.client.post(
553
553
  url, component.model_dump(mode="json", by_alias=True)
554
554
  )
@@ -774,7 +774,7 @@ class EntitiesEndpoint:
774
774
  Returns:
775
775
  PageProject: The page of projects belonging to the organization.
776
776
  """
777
- url = f"/api/{self.get_type()}/"
777
+ url = f"/api/{self.get_type()}"
778
778
  get_response = self.client.get(
779
779
  url, params={"filter": f"organization.id: '{organization.id}'"}
780
780
  )
@@ -1598,6 +1598,83 @@ class OrganizationEndpoint(Organization, EntityEndpoint):
1598
1598
  """
1599
1599
  return TaxonomiesEndpoint().set_client(self.client).set_organization(self)
1600
1600
 
1601
+ @property
1602
+ def available_templates(self, page=1, page_size=10, query="*"):
1603
+ """
1604
+ Get the available templates for the organization.
1605
+
1606
+ Returns:
1607
+ MarketplaceEndpoint: The marketplace endpoint of the organization.
1608
+ """
1609
+ url = f"/api/organizations/{self.id}/availableTemplates"
1610
+ response = self.client.get(url, params={"page": page, "pageSize": page_size, "query": query})
1611
+ return PageProjectTemplateEndpoint.model_validate(response.json()).set_client(self.client)
1612
+
1613
+ @property
1614
+ def available_models(self, page=1, page_size=10, query="*"):
1615
+ """
1616
+ Get the available models for the organization.
1617
+
1618
+ Returns:
1619
+ MarketplaceEndpoint: The marketplace endpoint of the organization.
1620
+ """
1621
+ url = f"/api/organizations/{self.id}/availableModels"
1622
+ response = self.client.get(url, params={"page": page, "pageSize": page_size, "query": query})
1623
+ return PageStoreEndpoint.model_validate(response.json()).set_client(self.client)
1624
+
1625
+ @property
1626
+ def available_assistants(self, page=1, page_size=10, query="*"):
1627
+ """
1628
+ Get the available assistants for the organization.
1629
+
1630
+ Returns:
1631
+ MarketplaceEndpoint: The marketplace endpoint of the organization.
1632
+ """
1633
+ url = f"/api/organizations/{self.id}/availableAssistants"
1634
+ response = self.client.get(url, params={"page": page, "pageSize": page_size, "query": query})
1635
+ return PageAssistantDefinitionEndpoint.model_validate(response.json()).set_client(self.client)
1636
+
1637
+ def get_subscriptions(self, page: int = 1, page_size: int = 10) -> "PageProductSubscriptionEndpoint":
1638
+ """
1639
+ Get the subscriptions of the organization.
1640
+
1641
+ Returns:
1642
+ The subscriptions of the organization.
1643
+ """
1644
+ url = f"/api/productSubscriptions"
1645
+ params = {
1646
+ "filter": f"organization.id: '{self.id}'",
1647
+ "page": page,
1648
+ "pageSize": page_size
1649
+ }
1650
+ response = self.client.get(url, params=params)
1651
+
1652
+ from kodexa.model.entities.product_subscription import PageProductSubscriptionEndpoint
1653
+ return PageProductSubscriptionEndpoint.model_validate(response.json()).set_client(self.client)
1654
+
1655
+ def remove_subscription(self, subscription: "ProductSubscription") -> None:
1656
+ """
1657
+ Remove a subscription from the organization.
1658
+
1659
+ Args:
1660
+ subscription_id (str): The id of the subscription to remove.
1661
+ """
1662
+ url = f"/api/productSubscriptions/{subscription.id}"
1663
+ self.client.delete(url)
1664
+
1665
+ def add_subscription(self, product: "Product") -> None:
1666
+ """
1667
+ Add a subscription to the organization.
1668
+
1669
+ Args:
1670
+ product (Product): The product to subscribe to.
1671
+ """
1672
+ url = f"/api/productSubscriptions"
1673
+ from kodexa.model.entities.product_subscription import ProductSubscription
1674
+ new_product_subscription = ProductSubscription(organization=self.detach(), product=product)
1675
+ print(new_product_subscription.model_dump_json(by_alias=True))
1676
+ self.client.post(url, body=json.loads(new_product_subscription.model_dump_json(by_alias=True)))
1677
+
1601
1678
 
1602
1679
  class ComponentsEndpoint(ClientEndpoint):
1603
1680
  """
@@ -1725,13 +1802,15 @@ class ComponentInstanceEndpoint(ClientEndpoint, SlugBasedMetadata):
1725
1802
  raise Exception(f"Component {self.ref} already exists")
1726
1803
 
1727
1804
  if exists:
1728
- self.client.put(url, self.model_dump(mode="json", by_alias=True))
1805
+ response = self.client.put(url, self.model_dump(mode="json", by_alias=True))
1806
+ process_response(response)
1729
1807
  return self.post_deploy()
1730
1808
 
1731
- self.client.post(
1809
+ response = self.client.post(
1732
1810
  f"/api/{self.get_type()}/{self.org_slug}",
1733
1811
  self.model_dump(mode="json", by_alias=True),
1734
1812
  )
1813
+ process_response(response)
1735
1814
  return self.post_deploy()
1736
1815
 
1737
1816
 
@@ -2072,6 +2151,33 @@ class ProjectTaxonomiesEndpoint(ProjectResourceEndpoint):
2072
2151
  return TaxonomyEndpoint
2073
2152
 
2074
2153
 
2154
+ class ProjectGuidanceSetsEndpoint(ProjectResourceEndpoint):
2155
+
2156
+ def get_type(self) -> str:
2157
+ return "guidance"
2158
+
2159
+ def get_instance_class(self, object_dict=None):
2160
+ return GuidanceSetsEndpoint
2161
+
2162
+
2163
+ class ProjectDataFormEndpoint(ProjectResourceEndpoint):
2164
+
2165
+ def get_type(self) -> str:
2166
+ return "dataForms"
2167
+
2168
+ def get_instance_class(self, object_dict=None):
2169
+ return DataFormEndpoint
2170
+
2171
+
2172
+ class ProjectDashboardEndpoint(ProjectResourceEndpoint):
2173
+
2174
+ def get_type(self) -> str:
2175
+ return "dashboards"
2176
+
2177
+ def get_instance_class(self, object_dict=None):
2178
+ return DashboardEndpoint
2179
+
2180
+
2075
2181
  class ProjectStoresEndpoint(ProjectResourceEndpoint):
2076
2182
  """Represents a project stores endpoint"""
2077
2183
 
@@ -2352,20 +2458,25 @@ class ProjectEndpoint(EntityEndpoint, Project):
2352
2458
  self,
2353
2459
  stores: List["StoreEndpoint"] = None,
2354
2460
  taxonomies: List["TaxonomyEndpoint"] = None,
2355
- ) -> "ProjectEndpoint":
2461
+ data_forms: List["DataFormEndpoint"] = None,
2462
+ guidance: List["GuidanceSetsEndpoint"] = None,
2463
+ dashboards: List["DashboardEndpoint"] = None,
2464
+ ):
2356
2465
  """Update the resources of the project.
2357
2466
 
2358
2467
  Args:
2359
2468
  stores (List["StoreEndpoint"], optional): List of store endpoints to update.
2360
2469
  taxonomies (List["TaxonomyEndpoint"], optional): List of taxonomy endpoints to update.
2361
-
2362
- Returns:
2363
- ProjectEndpoint: The updated project endpoint.
2470
+ data_forms (List["DataFormEndpoint"], optional): List of data form endpoints to update.
2471
+ guidance (List["GuidanceSetEndpoint"], optional): List of guidance set endpoints to update.
2472
+ dashboards (List["DashboardEndpoint"], optional): List of dashboard endpoints to update.
2364
2473
  """
2365
2474
  project_resources_update = ProjectResourcesUpdate()
2366
2475
  project_resources_update.store_refs = []
2367
2476
  project_resources_update.taxonomy_refs = []
2368
2477
  project_resources_update.dashboard_refs = []
2478
+ project_resources_update.data_form_refs = []
2479
+ project_resources_update.guidance_set_refs = []
2369
2480
 
2370
2481
  if stores:
2371
2482
  project_resources_update.store_refs = [store.ref for store in stores]
@@ -2374,6 +2485,18 @@ class ProjectEndpoint(EntityEndpoint, Project):
2374
2485
  project_resources_update.taxonomy_refs = [
2375
2486
  taxonomy.ref for taxonomy in taxonomies
2376
2487
  ]
2488
+ if data_forms:
2489
+ project_resources_update.data_form_refs = [
2490
+ data_form.ref for data_form in data_forms
2491
+ ]
2492
+ if guidance:
2493
+ project_resources_update.guidance_set_refs = [
2494
+ guidance.ref for guidance in guidance
2495
+ ]
2496
+ if dashboards:
2497
+ project_resources_update.dashboard_refs = [
2498
+ dashboard.ref for dashboard in dashboards
2499
+ ]
2377
2500
 
2378
2501
  self.client.put(
2379
2502
  f"/api/projects/{self.id}/resources",
@@ -2416,6 +2539,15 @@ class ProjectEndpoint(EntityEndpoint, Project):
2416
2539
  """
2417
2540
  return ProjectTaxonomiesEndpoint().set_client(self.client).set_project(self)
2418
2541
 
2542
+ @property
2543
+ def guidance(self) -> "ProjectGuidanceEndpoint":
2544
+ """Get the guidance sets endpoint of the project.
2545
+
2546
+ Returns:
2547
+ GuidanceSetsEndpoint: The guidance sets endpoint of the project.
2548
+ """
2549
+ return ProjectGuidanceSetEndpoint().set_client(self.client).set_project(self)
2550
+
2419
2551
  @property
2420
2552
  def assistants(self) -> ProjectAssistantsEndpoint:
2421
2553
  """Get the assistants endpoint of the project.
@@ -2450,6 +2582,7 @@ class ProjectEndpoint(EntityEndpoint, Project):
2450
2582
  return [ProjectTag.model_validate(tag) for tag in response.json()]
2451
2583
 
2452
2584
 
2585
+
2453
2586
  class MessagesEndpoint(EntitiesEndpoint):
2454
2587
  """Represents a message endpoint"""
2455
2588
 
@@ -2652,12 +2785,13 @@ class ProjectsEndpoint(EntitiesEndpoint):
2652
2785
  """
2653
2786
  return PageProjectEndpoint
2654
2787
 
2655
- def find_by_name(self, project_name: str) -> Optional[ProjectEndpoint]:
2788
+ def find_by_name(self, project_name: str, organization: Optional[Organization] = None) -> Optional[ProjectEndpoint]:
2656
2789
  """
2657
2790
  Find a project by name.
2658
2791
 
2659
2792
  Args:
2660
2793
  project_name (str): The name of the project to find.
2794
+ organization (Organization, optional): The organization to search in. Defaults to None.
2661
2795
 
2662
2796
  Returns:
2663
2797
  Optional[ProjectEndpoint]: The project endpoint if found, None otherwise.
@@ -2665,8 +2799,8 @@ class ProjectsEndpoint(EntitiesEndpoint):
2665
2799
 
2666
2800
  url = f"/api/{self.get_type()}"
2667
2801
  filters = {"filter": [f"name: '{project_name}'"]}
2668
- if self.organization is not None:
2669
- filters["filter"].append(f"organization.id: '{self.organization.id}'")
2802
+ if organization is not None:
2803
+ filters["filter"].append(f"organization.id: '{organization.id}'")
2670
2804
  get_response = self.client.get(url, params=filters)
2671
2805
  if len(get_response.json()["content"]) > 0:
2672
2806
  return ProjectEndpoint.model_validate(
@@ -2734,7 +2868,7 @@ class ProjectsEndpoint(EntitiesEndpoint):
2734
2868
  if self.organization is not None:
2735
2869
  params["filter"].append(f"organization.id: '{self.organization.id}'")
2736
2870
 
2737
- get_response = self.client.get(f"/api/{self.get_type()}/", params=params)
2871
+ get_response = self.client.get(f"/api/{self.get_type()}", params=params)
2738
2872
 
2739
2873
  return PageProjectEndpoint.model_validate(get_response.json()).set_client(
2740
2874
  self.client
@@ -2833,20 +2967,21 @@ class StoresEndpoint(ComponentEndpoint, ClientEndpoint, OrganizationOwned):
2833
2967
  raise ValueError(f"Unknown store type {object_dict['storeType']}")
2834
2968
 
2835
2969
 
2836
- class GuidanceEndpoint(ComponentEndpoint, ClientEndpoint, OrganizationOwned):
2970
+ class GuidanceSetsEndpoint(ComponentEndpoint, ClientEndpoint, OrganizationOwned):
2837
2971
  """
2838
- Represents a guidance endpoint.
2972
+ Represents a guidance set endpoint.
2839
2973
 
2840
- This class is used to interact with the prompts endpoint of the API.
2974
+ This class is used to interact with the guidance endpoint of the API.
2841
2975
  It provides methods to get the type, page class, and instance class of the endpoint,
2842
2976
  as well as to deploy an extension pack from a URL.
2843
2977
  """
2978
+
2844
2979
  def get_type(self) -> str:
2845
2980
  """
2846
2981
  Get the type of the endpoint.
2847
2982
 
2848
2983
  Returns:
2849
- str: The type of the endpoint, "prompts".
2984
+ str: The type of the endpoint, "guidance".
2850
2985
  """
2851
2986
  return "guidance"
2852
2987
 
@@ -2883,6 +3018,7 @@ class PromptsEndpoint(ComponentEndpoint, ClientEndpoint, OrganizationOwned):
2883
3018
  It provides methods to get the type, page class, and instance class of the endpoint,
2884
3019
  as well as to deploy an extension pack from a URL.
2885
3020
  """
3021
+
2886
3022
  def get_type(self) -> str:
2887
3023
  """
2888
3024
  Get the type of the endpoint.
@@ -4973,10 +5109,28 @@ class DataStoreEndpoint(StoreEndpoint):
4973
5109
 
4974
5110
 
4975
5111
  class DocumentStoreEndpoint(StoreEndpoint):
4976
- """Represents a document store that can be used to store files and their related document representations."""
4977
-
4978
5112
  """Represents a document store that can be used to store files and then their related document representations"""
4979
5113
 
5114
+ def query_by_embedding(self, embedding: list[float], threshold: float, limit: int):
5115
+ """
5116
+ Query the document store by an embedding.
5117
+
5118
+ Args:
5119
+ embedding (list[float]): The embedding to query by.
5120
+ threshold (int): The threshold to use for the query.
5121
+ limit (int): The limit of the query.
5122
+
5123
+ Returns:
5124
+ list[DocumentEmbedding]: a list of document embeddings
5125
+ """
5126
+ url = "/api/embeddings/query"
5127
+ embedding_query = {"embedding": embedding, "threshold": threshold, "limit": limit, "storeRef": self.ref}
5128
+ response = self.client.post(url, body=embedding_query)
5129
+ process_response(response)
5130
+
5131
+ # We get a list of the document embeddings
5132
+ return [DocumentEmbedding.model_validate(embedding) for embedding in response.json()]
5133
+
4980
5134
  def delete_by_path(self, object_path: str):
4981
5135
  """
4982
5136
  Delete the content stored in the store at the given path.
@@ -5271,6 +5425,8 @@ class DocumentStoreEndpoint(StoreEndpoint):
5271
5425
  count += 1
5272
5426
  if limit and count >= limit:
5273
5427
  break
5428
+ if limit and count >= limit:
5429
+ break
5274
5430
  page += 1
5275
5431
 
5276
5432
  def filter(
@@ -5357,6 +5513,7 @@ class DocumentStoreEndpoint(StoreEndpoint):
5357
5513
  f"api/stores/{self.ref.replace(':', '/')}/fs",
5358
5514
  params={"path": path, "meta": True},
5359
5515
  )
5516
+ process_response(get_response)
5360
5517
  return DocumentFamilyEndpoint.model_validate(get_response.json()).set_client(
5361
5518
  self.client
5362
5519
  )
@@ -5448,6 +5605,7 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5448
5605
  response = self.client.get(
5449
5606
  f"/api/stores/{self.ref.replace(':', '/')}/trainings/{training_id}/content"
5450
5607
  )
5608
+ process_response(response)
5451
5609
  from zipfile import ZipFile
5452
5610
  from io import BytesIO
5453
5611
 
@@ -5467,6 +5625,7 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5467
5625
  response = self.client.get(
5468
5626
  f"/api/stores/{self.ref.replace(':', '/')}/implementation"
5469
5627
  )
5628
+ process_response(response)
5470
5629
  from zipfile import ZipFile
5471
5630
  from io import BytesIO
5472
5631
 
@@ -5652,6 +5811,11 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5652
5811
  zipf.write(path_hit, relative_path)
5653
5812
  num_hits += 1
5654
5813
 
5814
+ if num_hits == 0:
5815
+ print(
5816
+ f"No files found for implementation in {metadata.base_dir} with {metadata.contents}"
5817
+ )
5818
+
5655
5819
  return num_hits
5656
5820
 
5657
5821
  def upload_contents(self, metadata: ModelContentMetadata, dry_run=False):
@@ -5669,10 +5833,11 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5669
5833
  num_hits = self.build_implementation_zip(metadata)
5670
5834
  if num_hits > 0 and not dry_run:
5671
5835
  with open("implementation.zip", "rb") as zip_content:
5672
- self.client.post(
5836
+ response = self.client.post(
5673
5837
  f"/api/stores/{self.ref.replace(':', '/')}/implementation",
5674
5838
  files={"implementation": zip_content},
5675
5839
  )
5840
+ process_response(response)
5676
5841
  results.append(f"{num_hits} files packaged and deployed to {self.ref}")
5677
5842
  if not metadata.keep_zip:
5678
5843
  Path("implementation.zip").unlink()
@@ -5754,6 +5919,8 @@ def process_response(response) -> requests.Response:
5754
5919
  raise Exception(f"Unauthorized ({response.text})")
5755
5920
  if response.status_code == 404:
5756
5921
  raise Exception(f"Not found ({response.text})")
5922
+ if response.status_code in [301, 302]:
5923
+ raise Exception(f"Redirected ({response.text})")
5757
5924
  if response.status_code == 405:
5758
5925
  raise Exception("Method not allowed")
5759
5926
  if response.status_code == 500:
@@ -5769,11 +5936,6 @@ def process_response(response) -> requests.Response:
5769
5936
 
5770
5937
  return response
5771
5938
 
5772
- #
5773
- # The Kodexa Client is the way that brings everything together
5774
- #
5775
- #
5776
-
5777
5939
 
5778
5940
  OBJECT_TYPES = {
5779
5941
  "extensionPacks": {
@@ -5829,7 +5991,7 @@ OBJECT_TYPES = {
5829
5991
  "name": "guidance",
5830
5992
  "plural": "guidance",
5831
5993
  "type": GuidanceSetEndpoint,
5832
- "endpoint": GuidanceEndpoint,
5994
+ "endpoint": GuidanceSetsEndpoint,
5833
5995
  },
5834
5996
  "modelRuntimes": {
5835
5997
  "name": "modelRuntime",
@@ -5969,9 +6131,10 @@ class ExtractionEngineEndpoint:
5969
6131
  """
5970
6132
  response = self.client.post(
5971
6133
  "/api/extractionEngine/extract",
5972
- data={"taxonomyJson": taxonomy.model_dump_json()},
6134
+ data={"taxonomyJson": taxonomy.model_dump_json(by_alias=True)},
5973
6135
  files={"document": document.to_kddb()},
5974
6136
  )
6137
+ print(response.json())
5975
6138
  return [
5976
6139
  DataObject.model_validate(data_object) for data_object in response.json()
5977
6140
  ]
@@ -5992,7 +6155,7 @@ class ExtractionEngineEndpoint:
5992
6155
  response = self.client.post(
5993
6156
  "/api/extractionEngine/extract",
5994
6157
  params="full",
5995
- data={"taxonomyJson": taxonomy.model_dump_json()},
6158
+ data={"taxonomyJson": taxonomy.model_dump_json(by_alias=True)},
5996
6159
  files={"document": document.to_kddb()},
5997
6160
  )
5998
6161
  return {
@@ -6023,7 +6186,7 @@ class ExtractionEngineEndpoint:
6023
6186
  response = self.client.post(
6024
6187
  "/api/extractionEngine/extract",
6025
6188
  params={"format": format},
6026
- data={"taxonomyJson": taxonomy.model_dump_json()},
6189
+ data={"taxonomyJson": taxonomy.model_dump_json(by_alias=True)},
6027
6190
  files={"document": document.to_kddb()},
6028
6191
  )
6029
6192
  return response.text
@@ -6046,7 +6209,7 @@ class KodexaClient:
6046
6209
  messages (MessagesEndpoint): An endpoint for messages.
6047
6210
  """
6048
6211
 
6049
- def __init__(self, url=None, access_token=None, profile="default"):
6212
+ def __init__(self, url=None, access_token=None, profile=None):
6050
6213
  from kodexa import KodexaPlatform
6051
6214
 
6052
6215
  self.base_url = url if url is not None else KodexaPlatform.get_url(profile)
@@ -6064,16 +6227,18 @@ class KodexaClient:
6064
6227
  self.channels = ChannelsEndpoint(self)
6065
6228
  self.assistants = AssistantsEndpoint(self)
6066
6229
  self.messages = MessagesEndpoint(self)
6230
+ from kodexa.model.entities.product import ProductsEndpoint
6231
+ self.products = ProductsEndpoint(self)
6232
+ self.guidance_sets = GuidanceSetsEndpoint(self)
6067
6233
 
6068
6234
  @staticmethod
6069
- def login(url, email, password):
6235
+ def login(url, token):
6070
6236
  """
6071
6237
  A static method to login to the Kodexa platform.
6072
6238
 
6073
6239
  Args:
6074
6240
  url (str): The URL for the Kodexa platform.
6075
- email (str): The email for the user.
6076
- password (str): The password for the user.
6241
+ token (str): The email for the user.
6077
6242
 
6078
6243
  Returns:
6079
6244
  KodexaClient: A KodexaClient instance.
@@ -6084,9 +6249,10 @@ class KodexaClient:
6084
6249
  from requests.auth import HTTPBasicAuth
6085
6250
 
6086
6251
  obj_response = requests.get(
6087
- f"{url}/api/account/me/token",
6088
- auth=HTTPBasicAuth(email, password),
6089
- headers={"content-type": "application/json"}
6252
+ f"{url}/api/account/me",
6253
+ headers={"content-type": "application/json",
6254
+ "x-access-token": token,
6255
+ "cf-access-token": os.environ.get("CF_TOKEN", "")}
6090
6256
  )
6091
6257
  if obj_response.status_code == 200:
6092
6258
  return KodexaClient(url, obj_response.text)
@@ -6218,6 +6384,7 @@ class KodexaClient:
6218
6384
  params=params,
6219
6385
  headers={
6220
6386
  "x-access-token": self.access_token,
6387
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6221
6388
  "content-type": "application/json",
6222
6389
  },
6223
6390
  )
@@ -6244,7 +6411,9 @@ class KodexaClient:
6244
6411
  params=params,
6245
6412
  headers={
6246
6413
  "x-access-token": self.access_token,
6414
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6247
6415
  "content-type": "application/json",
6416
+ "X-Requested-With": "XMLHttpRequest",
6248
6417
  }
6249
6418
  )
6250
6419
 
@@ -6266,7 +6435,10 @@ class KodexaClient:
6266
6435
  Returns:
6267
6436
  requests.Response: The response from the server.
6268
6437
  """
6269
- headers = {"x-access-token": self.access_token}
6438
+ headers = {
6439
+ "x-access-token": self.access_token,
6440
+ "X-Requested-With": "XMLHttpRequest",
6441
+ "cf-access-token": os.environ.get("CF_TOKEN", "")}
6270
6442
  if files is None:
6271
6443
  headers["content-type"] = "application/json"
6272
6444
 
@@ -6296,7 +6468,9 @@ class KodexaClient:
6296
6468
  Returns:
6297
6469
  requests.Response: The response from the server.
6298
6470
  """
6299
- headers = {"x-access-token": self.access_token}
6471
+ headers = {"x-access-token": self.access_token,
6472
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6473
+ "X-Requested-With": "XMLHttpRequest"}
6300
6474
  if files is None:
6301
6475
  headers["content-type"] = "application/json"
6302
6476
 
@@ -6324,7 +6498,9 @@ class KodexaClient:
6324
6498
  response = requests.delete(
6325
6499
  self.get_url(url),
6326
6500
  params=params,
6327
- headers={"x-access-token": self.access_token}
6501
+ headers={"x-access-token": self.access_token,
6502
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6503
+ "X-Requested-With": "XMLHttpRequest"}
6328
6504
  )
6329
6505
  return process_response(response)
6330
6506
 
@@ -6440,6 +6616,18 @@ class KodexaClient:
6440
6616
  )
6441
6617
  )
6442
6618
 
6619
+ for guidance in project.guidance.list():
6620
+ guidance_file = os.path.join(
6621
+ project_export_dir, f"guidance-{guidance.slug}-{guidance.version}.json"
6622
+ )
6623
+ with open(guidance_file, "w") as f:
6624
+ f.write(
6625
+ json.dumps(
6626
+ guidance.model_dump(mode="json", by_alias=True), indent=4
6627
+ )
6628
+ )
6629
+
6630
+
6443
6631
  def import_project(self, organization: OrganizationEndpoint, import_path: str):
6444
6632
  """
6445
6633
  A method to import a project.
@@ -6533,6 +6721,13 @@ class KodexaClient:
6533
6721
  taxonomy.ref = None
6534
6722
  taxonomies.append(organization.taxonomies.create(taxonomy))
6535
6723
 
6724
+ for guidance_file in glob.glob(os.path.join(import_path, "guidance-*.json")):
6725
+ with open(guidance_file, "r") as f:
6726
+ guidance = GuidanceSetEndpoint.model_validate(json.load(f))
6727
+ guidance.org_slug = None
6728
+ guidance.ref = None
6729
+ organization.guidance.create(guidance)
6730
+
6536
6731
  import time
6537
6732
 
6538
6733
  time.sleep(4)
@@ -6603,6 +6798,8 @@ class KodexaClient:
6603
6798
 
6604
6799
  raise Exception("A store must have a storeType")
6605
6800
 
6801
+ from kodexa.model.entities.product import ProductEndpoint
6802
+ from kodexa.model.entities.product_subscription import ProductSubscriptionEndpoint
6606
6803
  known_components = {
6607
6804
  "taxonomy": TaxonomyEndpoint,
6608
6805
  "pipeline": PipelineEndpoint,
@@ -6626,6 +6823,8 @@ class KodexaClient:
6626
6823
  "prompt": PromptEndpoint,
6627
6824
  "guidance": GuidanceSetEndpoint,
6628
6825
  "channel": ChannelEndpoint,
6826
+ "product": ProductEndpoint,
6827
+ "productSubscription": ProductSubscriptionEndpoint,
6629
6828
  }
6630
6829
 
6631
6830
  if component_type in known_components: