kodexa 7.0.1a8003164156__py3-none-any.whl → 7.0.1a9194120328__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 ProjectGuidanceEndpoint(ProjectResourceEndpoint):
2155
+
2156
+ def get_type(self) -> str:
2157
+ return "guidance"
2158
+
2159
+ def get_instance_class(self, object_dict=None):
2160
+ return GuidanceSetEndpoint
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
 
@@ -2252,7 +2358,9 @@ class ChannelEndpoint(EntityEndpoint, Channel):
2252
2358
  message_endpoint.content = message.content
2253
2359
  message_endpoint.block = message.block
2254
2360
  message_endpoint.feedback = message.feedback
2255
- message_endpoint.force_to_sender = message.force_to_sender
2361
+ message_endpoint.assistant = message.assistant
2362
+ message_endpoint.user = message.user
2363
+ message_endpoint.context = message.context
2256
2364
  return message_endpoint.create()
2257
2365
 
2258
2366
 
@@ -2350,20 +2458,25 @@ class ProjectEndpoint(EntityEndpoint, Project):
2350
2458
  self,
2351
2459
  stores: List["StoreEndpoint"] = None,
2352
2460
  taxonomies: List["TaxonomyEndpoint"] = None,
2353
- ) -> "ProjectEndpoint":
2461
+ data_forms: List["DataFormEndpoint"] = None,
2462
+ guidance: List["GuidanceSetEndpoint"] = None,
2463
+ dashboards: List["DashboardEndpoint"] = None,
2464
+ ):
2354
2465
  """Update the resources of the project.
2355
2466
 
2356
2467
  Args:
2357
2468
  stores (List["StoreEndpoint"], optional): List of store endpoints to update.
2358
2469
  taxonomies (List["TaxonomyEndpoint"], optional): List of taxonomy endpoints to update.
2359
-
2360
- Returns:
2361
- 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.
2362
2473
  """
2363
2474
  project_resources_update = ProjectResourcesUpdate()
2364
2475
  project_resources_update.store_refs = []
2365
2476
  project_resources_update.taxonomy_refs = []
2366
2477
  project_resources_update.dashboard_refs = []
2478
+ project_resources_update.data_form_refs = []
2479
+ project_resources_update.guidance_set_refs = []
2367
2480
 
2368
2481
  if stores:
2369
2482
  project_resources_update.store_refs = [store.ref for store in stores]
@@ -2372,6 +2485,18 @@ class ProjectEndpoint(EntityEndpoint, Project):
2372
2485
  project_resources_update.taxonomy_refs = [
2373
2486
  taxonomy.ref for taxonomy in taxonomies
2374
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
+ ]
2375
2500
 
2376
2501
  self.client.put(
2377
2502
  f"/api/projects/{self.id}/resources",
@@ -2414,6 +2539,15 @@ class ProjectEndpoint(EntityEndpoint, Project):
2414
2539
  """
2415
2540
  return ProjectTaxonomiesEndpoint().set_client(self.client).set_project(self)
2416
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 ProjectGuidanceEndpoint().set_client(self.client).set_project(self)
2550
+
2417
2551
  @property
2418
2552
  def assistants(self) -> ProjectAssistantsEndpoint:
2419
2553
  """Get the assistants endpoint of the project.
@@ -2448,6 +2582,43 @@ class ProjectEndpoint(EntityEndpoint, Project):
2448
2582
  return [ProjectTag.model_validate(tag) for tag in response.json()]
2449
2583
 
2450
2584
 
2585
+ class GuidanceSetsEndpoint(EntitiesEndpoint):
2586
+ """Represents a message endpoint"""
2587
+
2588
+ def get_type(self) -> str:
2589
+ """
2590
+ Get the type of the endpoint.
2591
+
2592
+ Returns:
2593
+ str: The type of the endpoint, in this case "guidance".
2594
+ """
2595
+ return "guidance"
2596
+
2597
+ def get_instance_class(self, object_dict=None):
2598
+ """
2599
+ Get the instance class of the endpoint.
2600
+
2601
+ Args:
2602
+ object_dict (dict, optional): An optional dictionary object. Defaults to None.
2603
+
2604
+ Returns:
2605
+ GuidanceSetEndpoint: The instance class of the endpoint.
2606
+ """
2607
+ return GuidanceSetEndpoint
2608
+
2609
+ def get_page_class(self, object_dict=None):
2610
+ """
2611
+ Get the page class of the endpoint.
2612
+
2613
+ Args:
2614
+ object_dict (dict, optional): An optional dictionary object. Defaults to None.
2615
+
2616
+ Returns:
2617
+ PageGuidanceSetEndpoint: The page class of the endpoint.
2618
+ """
2619
+ return PageGuidanceSetEndpoint
2620
+
2621
+
2451
2622
  class MessagesEndpoint(EntitiesEndpoint):
2452
2623
  """Represents a message endpoint"""
2453
2624
 
@@ -2650,12 +2821,13 @@ class ProjectsEndpoint(EntitiesEndpoint):
2650
2821
  """
2651
2822
  return PageProjectEndpoint
2652
2823
 
2653
- def find_by_name(self, project_name: str) -> Optional[ProjectEndpoint]:
2824
+ def find_by_name(self, project_name: str, organization: Optional[Organization] = None) -> Optional[ProjectEndpoint]:
2654
2825
  """
2655
2826
  Find a project by name.
2656
2827
 
2657
2828
  Args:
2658
2829
  project_name (str): The name of the project to find.
2830
+ organization (Organization, optional): The organization to search in. Defaults to None.
2659
2831
 
2660
2832
  Returns:
2661
2833
  Optional[ProjectEndpoint]: The project endpoint if found, None otherwise.
@@ -2663,8 +2835,8 @@ class ProjectsEndpoint(EntitiesEndpoint):
2663
2835
 
2664
2836
  url = f"/api/{self.get_type()}"
2665
2837
  filters = {"filter": [f"name: '{project_name}'"]}
2666
- if self.organization is not None:
2667
- filters["filter"].append(f"organization.id: '{self.organization.id}'")
2838
+ if organization is not None:
2839
+ filters["filter"].append(f"organization.id: '{organization.id}'")
2668
2840
  get_response = self.client.get(url, params=filters)
2669
2841
  if len(get_response.json()["content"]) > 0:
2670
2842
  return ProjectEndpoint.model_validate(
@@ -2732,7 +2904,7 @@ class ProjectsEndpoint(EntitiesEndpoint):
2732
2904
  if self.organization is not None:
2733
2905
  params["filter"].append(f"organization.id: '{self.organization.id}'")
2734
2906
 
2735
- get_response = self.client.get(f"/api/{self.get_type()}/", params=params)
2907
+ get_response = self.client.get(f"/api/{self.get_type()}", params=params)
2736
2908
 
2737
2909
  return PageProjectEndpoint.model_validate(get_response.json()).set_client(
2738
2910
  self.client
@@ -2835,16 +3007,17 @@ class GuidanceEndpoint(ComponentEndpoint, ClientEndpoint, OrganizationOwned):
2835
3007
  """
2836
3008
  Represents a guidance endpoint.
2837
3009
 
2838
- This class is used to interact with the prompts endpoint of the API.
3010
+ This class is used to interact with the guidance endpoint of the API.
2839
3011
  It provides methods to get the type, page class, and instance class of the endpoint,
2840
3012
  as well as to deploy an extension pack from a URL.
2841
3013
  """
3014
+
2842
3015
  def get_type(self) -> str:
2843
3016
  """
2844
3017
  Get the type of the endpoint.
2845
3018
 
2846
3019
  Returns:
2847
- str: The type of the endpoint, "prompts".
3020
+ str: The type of the endpoint, "guidance".
2848
3021
  """
2849
3022
  return "guidance"
2850
3023
 
@@ -2881,6 +3054,7 @@ class PromptsEndpoint(ComponentEndpoint, ClientEndpoint, OrganizationOwned):
2881
3054
  It provides methods to get the type, page class, and instance class of the endpoint,
2882
3055
  as well as to deploy an extension pack from a URL.
2883
3056
  """
3057
+
2884
3058
  def get_type(self) -> str:
2885
3059
  """
2886
3060
  Get the type of the endpoint.
@@ -4971,10 +5145,28 @@ class DataStoreEndpoint(StoreEndpoint):
4971
5145
 
4972
5146
 
4973
5147
  class DocumentStoreEndpoint(StoreEndpoint):
4974
- """Represents a document store that can be used to store files and their related document representations."""
4975
-
4976
5148
  """Represents a document store that can be used to store files and then their related document representations"""
4977
5149
 
5150
+ def query_by_embedding(self, embedding: list[float], threshold: float, limit: int):
5151
+ """
5152
+ Query the document store by an embedding.
5153
+
5154
+ Args:
5155
+ embedding (list[float]): The embedding to query by.
5156
+ threshold (int): The threshold to use for the query.
5157
+ limit (int): The limit of the query.
5158
+
5159
+ Returns:
5160
+ list[DocumentEmbedding]: a list of document embeddings
5161
+ """
5162
+ url = "/api/embeddings/query"
5163
+ embedding_query = {"embedding": embedding, "threshold": threshold, "limit": limit, "storeRef": self.ref}
5164
+ response = self.client.post(url, body=embedding_query)
5165
+ process_response(response)
5166
+
5167
+ # We get a list of the document embeddings
5168
+ return [DocumentEmbedding.model_validate(embedding) for embedding in response.json()]
5169
+
4978
5170
  def delete_by_path(self, object_path: str):
4979
5171
  """
4980
5172
  Delete the content stored in the store at the given path.
@@ -5269,6 +5461,8 @@ class DocumentStoreEndpoint(StoreEndpoint):
5269
5461
  count += 1
5270
5462
  if limit and count >= limit:
5271
5463
  break
5464
+ if limit and count >= limit:
5465
+ break
5272
5466
  page += 1
5273
5467
 
5274
5468
  def filter(
@@ -5355,6 +5549,7 @@ class DocumentStoreEndpoint(StoreEndpoint):
5355
5549
  f"api/stores/{self.ref.replace(':', '/')}/fs",
5356
5550
  params={"path": path, "meta": True},
5357
5551
  )
5552
+ process_response(get_response)
5358
5553
  return DocumentFamilyEndpoint.model_validate(get_response.json()).set_client(
5359
5554
  self.client
5360
5555
  )
@@ -5446,6 +5641,7 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5446
5641
  response = self.client.get(
5447
5642
  f"/api/stores/{self.ref.replace(':', '/')}/trainings/{training_id}/content"
5448
5643
  )
5644
+ process_response(response)
5449
5645
  from zipfile import ZipFile
5450
5646
  from io import BytesIO
5451
5647
 
@@ -5465,6 +5661,7 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5465
5661
  response = self.client.get(
5466
5662
  f"/api/stores/{self.ref.replace(':', '/')}/implementation"
5467
5663
  )
5664
+ process_response(response)
5468
5665
  from zipfile import ZipFile
5469
5666
  from io import BytesIO
5470
5667
 
@@ -5650,6 +5847,11 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5650
5847
  zipf.write(path_hit, relative_path)
5651
5848
  num_hits += 1
5652
5849
 
5850
+ if num_hits == 0:
5851
+ print(
5852
+ f"No files found for implementation in {metadata.base_dir} with {metadata.contents}"
5853
+ )
5854
+
5653
5855
  return num_hits
5654
5856
 
5655
5857
  def upload_contents(self, metadata: ModelContentMetadata, dry_run=False):
@@ -5667,10 +5869,11 @@ class ModelStoreEndpoint(DocumentStoreEndpoint):
5667
5869
  num_hits = self.build_implementation_zip(metadata)
5668
5870
  if num_hits > 0 and not dry_run:
5669
5871
  with open("implementation.zip", "rb") as zip_content:
5670
- self.client.post(
5872
+ response = self.client.post(
5671
5873
  f"/api/stores/{self.ref.replace(':', '/')}/implementation",
5672
5874
  files={"implementation": zip_content},
5673
5875
  )
5876
+ process_response(response)
5674
5877
  results.append(f"{num_hits} files packaged and deployed to {self.ref}")
5675
5878
  if not metadata.keep_zip:
5676
5879
  Path("implementation.zip").unlink()
@@ -5752,6 +5955,8 @@ def process_response(response) -> requests.Response:
5752
5955
  raise Exception(f"Unauthorized ({response.text})")
5753
5956
  if response.status_code == 404:
5754
5957
  raise Exception(f"Not found ({response.text})")
5958
+ if response.status_code in [301, 302]:
5959
+ raise Exception(f"Redirected ({response.text})")
5755
5960
  if response.status_code == 405:
5756
5961
  raise Exception("Method not allowed")
5757
5962
  if response.status_code == 500:
@@ -5767,11 +5972,6 @@ def process_response(response) -> requests.Response:
5767
5972
 
5768
5973
  return response
5769
5974
 
5770
- #
5771
- # The Kodexa Client is the way that brings everything together
5772
- #
5773
- #
5774
-
5775
5975
 
5776
5976
  OBJECT_TYPES = {
5777
5977
  "extensionPacks": {
@@ -5967,9 +6167,10 @@ class ExtractionEngineEndpoint:
5967
6167
  """
5968
6168
  response = self.client.post(
5969
6169
  "/api/extractionEngine/extract",
5970
- data={"taxonomyJson": taxonomy.model_dump_json()},
6170
+ data={"taxonomyJson": taxonomy.model_dump_json(by_alias=True)},
5971
6171
  files={"document": document.to_kddb()},
5972
6172
  )
6173
+ print(response.json())
5973
6174
  return [
5974
6175
  DataObject.model_validate(data_object) for data_object in response.json()
5975
6176
  ]
@@ -5990,7 +6191,7 @@ class ExtractionEngineEndpoint:
5990
6191
  response = self.client.post(
5991
6192
  "/api/extractionEngine/extract",
5992
6193
  params="full",
5993
- data={"taxonomyJson": taxonomy.model_dump_json()},
6194
+ data={"taxonomyJson": taxonomy.model_dump_json(by_alias=True)},
5994
6195
  files={"document": document.to_kddb()},
5995
6196
  )
5996
6197
  return {
@@ -6021,7 +6222,7 @@ class ExtractionEngineEndpoint:
6021
6222
  response = self.client.post(
6022
6223
  "/api/extractionEngine/extract",
6023
6224
  params={"format": format},
6024
- data={"taxonomyJson": taxonomy.model_dump_json()},
6225
+ data={"taxonomyJson": taxonomy.model_dump_json(by_alias=True)},
6025
6226
  files={"document": document.to_kddb()},
6026
6227
  )
6027
6228
  return response.text
@@ -6044,7 +6245,7 @@ class KodexaClient:
6044
6245
  messages (MessagesEndpoint): An endpoint for messages.
6045
6246
  """
6046
6247
 
6047
- def __init__(self, url=None, access_token=None, profile="default"):
6248
+ def __init__(self, url=None, access_token=None, profile=None):
6048
6249
  from kodexa import KodexaPlatform
6049
6250
 
6050
6251
  self.base_url = url if url is not None else KodexaPlatform.get_url(profile)
@@ -6062,16 +6263,18 @@ class KodexaClient:
6062
6263
  self.channels = ChannelsEndpoint(self)
6063
6264
  self.assistants = AssistantsEndpoint(self)
6064
6265
  self.messages = MessagesEndpoint(self)
6266
+ from kodexa.model.entities.product import ProductsEndpoint
6267
+ self.products = ProductsEndpoint(self)
6268
+ self.guidance_sets = GuidanceSetsEndpoint(self)
6065
6269
 
6066
6270
  @staticmethod
6067
- def login(url, email, password):
6271
+ def login(url, token):
6068
6272
  """
6069
6273
  A static method to login to the Kodexa platform.
6070
6274
 
6071
6275
  Args:
6072
6276
  url (str): The URL for the Kodexa platform.
6073
- email (str): The email for the user.
6074
- password (str): The password for the user.
6277
+ token (str): The email for the user.
6075
6278
 
6076
6279
  Returns:
6077
6280
  KodexaClient: A KodexaClient instance.
@@ -6082,9 +6285,10 @@ class KodexaClient:
6082
6285
  from requests.auth import HTTPBasicAuth
6083
6286
 
6084
6287
  obj_response = requests.get(
6085
- f"{url}/api/account/me/token",
6086
- auth=HTTPBasicAuth(email, password),
6087
- headers={"content-type": "application/json"}
6288
+ f"{url}/api/account/me",
6289
+ headers={"content-type": "application/json",
6290
+ "x-access-token": token,
6291
+ "cf-access-token": os.environ.get("CF_TOKEN", "")}
6088
6292
  )
6089
6293
  if obj_response.status_code == 200:
6090
6294
  return KodexaClient(url, obj_response.text)
@@ -6216,6 +6420,7 @@ class KodexaClient:
6216
6420
  params=params,
6217
6421
  headers={
6218
6422
  "x-access-token": self.access_token,
6423
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6219
6424
  "content-type": "application/json",
6220
6425
  },
6221
6426
  )
@@ -6242,7 +6447,9 @@ class KodexaClient:
6242
6447
  params=params,
6243
6448
  headers={
6244
6449
  "x-access-token": self.access_token,
6450
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6245
6451
  "content-type": "application/json",
6452
+ "X-Requested-With": "XMLHttpRequest",
6246
6453
  }
6247
6454
  )
6248
6455
 
@@ -6264,7 +6471,10 @@ class KodexaClient:
6264
6471
  Returns:
6265
6472
  requests.Response: The response from the server.
6266
6473
  """
6267
- headers = {"x-access-token": self.access_token}
6474
+ headers = {
6475
+ "x-access-token": self.access_token,
6476
+ "X-Requested-With": "XMLHttpRequest",
6477
+ "cf-access-token": os.environ.get("CF_TOKEN", "")}
6268
6478
  if files is None:
6269
6479
  headers["content-type"] = "application/json"
6270
6480
 
@@ -6294,7 +6504,9 @@ class KodexaClient:
6294
6504
  Returns:
6295
6505
  requests.Response: The response from the server.
6296
6506
  """
6297
- headers = {"x-access-token": self.access_token}
6507
+ headers = {"x-access-token": self.access_token,
6508
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6509
+ "X-Requested-With": "XMLHttpRequest"}
6298
6510
  if files is None:
6299
6511
  headers["content-type"] = "application/json"
6300
6512
 
@@ -6322,7 +6534,9 @@ class KodexaClient:
6322
6534
  response = requests.delete(
6323
6535
  self.get_url(url),
6324
6536
  params=params,
6325
- headers={"x-access-token": self.access_token}
6537
+ headers={"x-access-token": self.access_token,
6538
+ "cf-access-token": os.environ.get("CF_TOKEN", ""),
6539
+ "X-Requested-With": "XMLHttpRequest"}
6326
6540
  )
6327
6541
  return process_response(response)
6328
6542
 
@@ -6438,6 +6652,18 @@ class KodexaClient:
6438
6652
  )
6439
6653
  )
6440
6654
 
6655
+ for guidance in project.guidance.list():
6656
+ guidance_file = os.path.join(
6657
+ project_export_dir, f"guidance-{guidance.slug}-{guidance.version}.json"
6658
+ )
6659
+ with open(guidance_file, "w") as f:
6660
+ f.write(
6661
+ json.dumps(
6662
+ guidance.model_dump(mode="json", by_alias=True), indent=4
6663
+ )
6664
+ )
6665
+
6666
+
6441
6667
  def import_project(self, organization: OrganizationEndpoint, import_path: str):
6442
6668
  """
6443
6669
  A method to import a project.
@@ -6531,6 +6757,13 @@ class KodexaClient:
6531
6757
  taxonomy.ref = None
6532
6758
  taxonomies.append(organization.taxonomies.create(taxonomy))
6533
6759
 
6760
+ for guidance_file in glob.glob(os.path.join(import_path, "guidance-*.json")):
6761
+ with open(guidance_file, "r") as f:
6762
+ guidance = GuidanceSetEndpoint.model_validate(json.load(f))
6763
+ guidance.org_slug = None
6764
+ guidance.ref = None
6765
+ organization.guidance.create(guidance)
6766
+
6534
6767
  import time
6535
6768
 
6536
6769
  time.sleep(4)
@@ -6601,6 +6834,8 @@ class KodexaClient:
6601
6834
 
6602
6835
  raise Exception("A store must have a storeType")
6603
6836
 
6837
+ from kodexa.model.entities.product import ProductEndpoint
6838
+ from kodexa.model.entities.product_subscription import ProductSubscriptionEndpoint
6604
6839
  known_components = {
6605
6840
  "taxonomy": TaxonomyEndpoint,
6606
6841
  "pipeline": PipelineEndpoint,
@@ -6624,6 +6859,8 @@ class KodexaClient:
6624
6859
  "prompt": PromptEndpoint,
6625
6860
  "guidance": GuidanceSetEndpoint,
6626
6861
  "channel": ChannelEndpoint,
6862
+ "product": ProductEndpoint,
6863
+ "productSubscription": ProductSubscriptionEndpoint,
6627
6864
  }
6628
6865
 
6629
6866
  if component_type in known_components: