luna-quantum 0.0.29__py3-none-any.whl → 0.0.37__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.

Potentially problematic release.


This version of luna-quantum might be problematic. Click here for more details.

Files changed (36) hide show
  1. {luna_quantum-0.0.29.dist-info → luna_quantum-0.0.37.dist-info}/METADATA +1 -1
  2. {luna_quantum-0.0.29.dist-info → luna_quantum-0.0.37.dist-info}/RECORD +35 -33
  3. luna_sdk/controllers/luna_http_client.py +27 -0
  4. luna_sdk/controllers/luna_platform_client.py +34 -16
  5. luna_sdk/controllers/luna_q.py +5 -7
  6. luna_sdk/controllers/luna_solve.py +9 -7
  7. luna_sdk/controllers/luna_transform.py +13 -7
  8. luna_sdk/error/http_error_utils.py +10 -3
  9. luna_sdk/exceptions/encryption_exception.py +0 -6
  10. luna_sdk/interfaces/circuit_repo_i.py +0 -6
  11. luna_sdk/interfaces/optimization_repo_i.py +5 -4
  12. luna_sdk/interfaces/qpu_token_repo_i.py +18 -18
  13. luna_sdk/interfaces/solutions_repo_i.py +0 -6
  14. luna_sdk/repositories/circuit_repo.py +0 -11
  15. luna_sdk/repositories/optimization_repo.py +11 -10
  16. luna_sdk/repositories/qpu_token_repo.py +36 -27
  17. luna_sdk/repositories/solutions_repo.py +0 -8
  18. luna_sdk/schemas/create/circuit.py +0 -3
  19. luna_sdk/schemas/create/qpu_token.py +0 -3
  20. luna_sdk/schemas/create/solution.py +0 -1
  21. luna_sdk/schemas/enums/optimization.py +8 -7
  22. luna_sdk/schemas/enums/qpu_token_type.py +1 -1
  23. luna_sdk/schemas/optimization.py +53 -15
  24. luna_sdk/schemas/optimization_formats/qubo.py +8 -0
  25. luna_sdk/schemas/qpu_token.py +1 -5
  26. luna_sdk/schemas/rest/qpu_token/qpu_token_source.py +18 -0
  27. luna_sdk/schemas/rest/qpu_token/token_provider.py +47 -15
  28. luna_sdk/schemas/solution.py +89 -35
  29. luna_sdk/schemas/solver_parameters/dwave/base.py +4 -1
  30. luna_sdk/schemas/solver_parameters/dwave/quantum_annealing.py +13 -0
  31. luna_sdk/schemas/solver_parameters/dwave/repeated_reverse_quantum_annealing.py +13 -1
  32. luna_sdk/schemas/solver_parameters/dwave/tabu_search.py +2 -4
  33. luna_sdk/utils/qpu_tokens.py +14 -13
  34. luna_sdk/controllers/custom_login_client.py +0 -66
  35. {luna_quantum-0.0.29.dist-info → luna_quantum-0.0.37.dist-info}/LICENSE +0 -0
  36. {luna_quantum-0.0.29.dist-info → luna_quantum-0.0.37.dist-info}/WHEEL +0 -0
@@ -1,7 +1,6 @@
1
1
  import os
2
2
  from typing import Any, Dict, Optional
3
3
 
4
- from luna_sdk.exceptions.encryption_exception import EncryptionNotSetException
5
4
  from luna_sdk.interfaces.circuit_repo_i import ICircuitRepo
6
5
  from luna_sdk.schemas.circuit import CircuitJob, CircuitResult
7
6
  from luna_sdk.schemas.create.circuit import CircuitIn
@@ -20,7 +19,6 @@ class CircuitRepo(ICircuitRepo):
20
19
  provider: CircuitProviderEnum,
21
20
  params: Dict[str, Any] = {},
22
21
  qpu_tokens: Optional[TokenProvider] = None,
23
- encryption_key: Optional[str] = None,
24
22
  **kwargs,
25
23
  ) -> CircuitJob:
26
24
  if qpu_tokens is not None:
@@ -34,15 +32,11 @@ class CircuitRepo(ICircuitRepo):
34
32
  if rest_qpu_tokens is None:
35
33
  rest_qpu_tokens = extract_qpu_tokens_from_env()
36
34
 
37
- encryption_key = encryption_key or os.environ.get("LUNA_ENCRYPTION_KEY")
38
- if encryption_key is None:
39
- raise EncryptionNotSetException
40
35
  circuit_in: CircuitIn = CircuitIn(
41
36
  provider=provider,
42
37
  circuit=circuit,
43
38
  params=params,
44
39
  qpu_tokens=rest_qpu_tokens,
45
- encryption_key=encryption_key,
46
40
  )
47
41
 
48
42
  response = self._client.post(
@@ -55,16 +49,11 @@ class CircuitRepo(ICircuitRepo):
55
49
  def get(
56
50
  self,
57
51
  job: CircuitJob,
58
- encryption_key: Optional[str] = None,
59
52
  **kwargs,
60
53
  ) -> CircuitResult:
61
54
  url = f"{self._endpoint}/{job.id}/{job.provider.value}"
62
- encryption_key = encryption_key or os.environ.get("LUNA_ENCRYPTION_KEY")
63
- if encryption_key is None:
64
- raise EncryptionNotSetException
65
55
  if job.params is None:
66
56
  job.params = {}
67
- job.params["encryption_key"] = encryption_key
68
57
  response = self._client.get(url, params=job.params, **kwargs)
69
58
 
70
59
  response.raise_for_status()
@@ -9,7 +9,7 @@ from luna_sdk.interfaces.optimization_repo_i import IOptimizationRepo
9
9
  from luna_sdk.schemas import UseCase
10
10
  from luna_sdk.schemas.create import QUBOIn
11
11
  from luna_sdk.schemas.create.optimization import OptimizationUseCaseIn
12
- from luna_sdk.schemas.enums.optimization import InputType
12
+ from luna_sdk.schemas.enums.optimization import OptFormat
13
13
  from luna_sdk.schemas.enums.timeframe import TimeframeEnum
14
14
  from luna_sdk.schemas.optimization import (
15
15
  Optimization,
@@ -17,6 +17,7 @@ from luna_sdk.schemas.optimization import (
17
17
  OptimizationCQM,
18
18
  OptimizationLP,
19
19
  OptimizationUseCase,
20
+ OptimizationQubo,
20
21
  )
21
22
  from luna_sdk.schemas.optimization_formats.bqm import BQMSchema
22
23
  from luna_sdk.schemas.optimization_formats.cqm import CQMSchema
@@ -30,7 +31,7 @@ class OptimizationRepo(IOptimizationRepo):
30
31
  def get_all(
31
32
  self,
32
33
  timeframe: Optional[TimeframeEnum] = None,
33
- input_type: Optional[InputType] = None,
34
+ input_type: Optional[OptFormat] = None,
34
35
  limit: int = 50,
35
36
  offset: int = 0,
36
37
  **kwargs,
@@ -40,7 +41,7 @@ class OptimizationRepo(IOptimizationRepo):
40
41
  params["timeframe"] = timeframe.value
41
42
 
42
43
  if input_type:
43
- params["input_type"] = input_type.value
44
+ params["original_format"] = input_type.value
44
45
 
45
46
  if limit < 1:
46
47
  # set the minimum limit to 1
@@ -63,21 +64,21 @@ class OptimizationRepo(IOptimizationRepo):
63
64
 
64
65
  optimization_data = response_data.pop("optimization_data", None)
65
66
  if optimization_data:
66
- input_type = response_data["input_type"]
67
+ original_format = response_data["original_format"]
67
68
 
68
- if input_type in (InputType.bqm_spin, InputType.bqm_binary):
69
+ if original_format == OptFormat.BQM:
69
70
  model = OptimizationBQM
70
- elif input_type == InputType.cqm:
71
+ elif original_format == OptFormat.CQM:
71
72
  model = OptimizationCQM
72
- elif input_type == InputType.lp:
73
+ elif original_format == OptFormat.LP:
73
74
  model = OptimizationLP
74
- elif input_type == InputType.qubo:
75
+ elif original_format == OptFormat.QUBO:
75
76
  if response_data.get("use_case_name"):
76
77
  model = OptimizationUseCase
77
78
  else:
78
- model = OptimizationBQM
79
+ model = OptimizationQubo
79
80
  else:
80
- model = OptimizationBQM
81
+ raise ValueError("Unknown optimization format")
81
82
 
82
83
  response_data.update(optimization_data)
83
84
 
@@ -3,14 +3,18 @@ import os
3
3
  from typing import Dict, List, Optional
4
4
 
5
5
  from httpx import Response
6
+ from pydantic import TypeAdapter
6
7
 
7
- from luna_sdk.exceptions.encryption_exception import EncryptionNotSetException
8
8
  from luna_sdk.interfaces.qpu_token_repo_i import IQpuTokenRepo
9
9
  from luna_sdk.schemas import QpuTokenOut
10
10
  from luna_sdk.schemas.create import QpuTokenIn
11
11
  from luna_sdk.schemas.enums.qpu_token_type import QpuTokenTypeEnum
12
12
 
13
13
 
14
+ _ORGANIZATION_QPU_TOKENS_BACKEND = "shared"
15
+ _PERSONAL_QPU_TOKENS_BACKEND = "private"
16
+
17
+
14
18
  class QpuTokenRepo(IQpuTokenRepo):
15
19
  @property
16
20
  def _endpoint(self) -> str:
@@ -22,15 +26,15 @@ class QpuTokenRepo(IQpuTokenRepo):
22
26
  if token_type is None:
23
27
  return f"{self._endpoint}"
24
28
  elif token_type == QpuTokenTypeEnum.PERSONAL:
25
- return f"{self._endpoint}/users"
29
+ return f"{self._endpoint}/{_PERSONAL_QPU_TOKENS_BACKEND}"
26
30
  else:
27
- return f"{self._endpoint}/organization"
31
+ return f"{self._endpoint}/{_ORGANIZATION_QPU_TOKENS_BACKEND}"
28
32
 
29
33
  def _get_by_name(
30
34
  self, name: str, token_type: QpuTokenTypeEnum, **kwargs
31
35
  ) -> QpuTokenOut:
32
36
  response: Response = self._client.get(
33
- f"{self._get_endpoint_by_type(token_type)}/by_name/{name}", **kwargs
37
+ f"{self._get_endpoint_by_type(token_type)}/{name}", **kwargs
34
38
  )
35
39
  response.raise_for_status()
36
40
 
@@ -44,17 +48,12 @@ class QpuTokenRepo(IQpuTokenRepo):
44
48
  provider: str,
45
49
  token: str,
46
50
  token_type: QpuTokenTypeEnum,
47
- encryption_key: Optional[str] = None,
48
51
  **kwargs,
49
52
  ) -> QpuTokenOut:
50
- encryption_key = encryption_key or os.environ.get("LUNA_ENCRYPTION_KEY")
51
- if encryption_key is None:
52
- raise EncryptionNotSetException
53
53
  qpu_token = QpuTokenIn(
54
54
  name=name,
55
55
  provider=provider,
56
56
  token=token,
57
- encryption_key=encryption_key,
58
57
  )
59
58
 
60
59
  response: Response = self._client.post(
@@ -70,30 +69,42 @@ class QpuTokenRepo(IQpuTokenRepo):
70
69
  def get_all(
71
70
  self,
72
71
  filter_provider: Optional[str] = None,
73
- name: Optional[str] = None,
74
72
  token_type: Optional[QpuTokenTypeEnum] = None,
73
+ limit: Optional[int] = None,
74
+ offset: Optional[int] = None,
75
75
  **kwargs,
76
76
  ) -> Dict[QpuTokenTypeEnum, List[QpuTokenOut]]:
77
77
  params = {}
78
78
  if filter_provider:
79
79
  params["filter_provider"] = filter_provider
80
80
 
81
- if name:
82
- params["name"] = name
81
+ if limit is not None:
82
+ params["limit"] = str(limit)
83
+ if offset is not None:
84
+ params["offset"] = str(offset)
85
+ if token_type == QpuTokenTypeEnum.PERSONAL:
86
+ params["token_type"] = _PERSONAL_QPU_TOKENS_BACKEND
87
+ if token_type == QpuTokenTypeEnum.GROUP:
88
+ params["token_type"] = _ORGANIZATION_QPU_TOKENS_BACKEND
83
89
 
84
90
  response = self._client.get(
85
- self._get_endpoint_by_type(), params=params, **kwargs
91
+ self._endpoint,
92
+ params=params,
93
+ **kwargs,
86
94
  )
87
- response.raise_for_status()
88
-
95
+ ta = TypeAdapter(List[QpuTokenOut])
89
96
  to_return: Dict[QpuTokenTypeEnum, List[QpuTokenOut]] = {}
90
- for key, value in response.json().items():
91
- tokens = []
92
- for item in value:
93
- if token_type is None or token_type.value == key:
94
- item["token_type"] = QpuTokenTypeEnum(key)
95
- tokens.append(QpuTokenOut.model_validate(item))
96
- to_return[QpuTokenTypeEnum(key)] = tokens
97
+ resp = response.json()
98
+
99
+ shared_tokens = resp.get(_ORGANIZATION_QPU_TOKENS_BACKEND, [])
100
+ for qpu_token in shared_tokens:
101
+ qpu_token["token_type"] = QpuTokenTypeEnum.GROUP
102
+ to_return[QpuTokenTypeEnum.GROUP] = ta.validate_python(shared_tokens)
103
+
104
+ personal_tokens = resp.get(_PERSONAL_QPU_TOKENS_BACKEND, [])
105
+ for qpu_token in personal_tokens:
106
+ qpu_token["token_type"] = QpuTokenTypeEnum.PERSONAL
107
+ to_return[QpuTokenTypeEnum.PERSONAL] = ta.validate_python(personal_tokens)
97
108
 
98
109
  return to_return
99
110
 
@@ -114,8 +125,8 @@ class QpuTokenRepo(IQpuTokenRepo):
114
125
 
115
126
  token: QpuTokenOut = self.get(name, token_type)
116
127
 
117
- response = self._client.put(
118
- f"{self._get_endpoint_by_type(token_type)}/{token.id}",
128
+ response = self._client.patch(
129
+ f"{self._get_endpoint_by_type(token_type)}/{token.name}",
119
130
  content=json.dumps(qpu_token_update_data),
120
131
  **kwargs,
121
132
  )
@@ -126,9 +137,7 @@ class QpuTokenRepo(IQpuTokenRepo):
126
137
  return QpuTokenOut.model_validate(qpu_token_data)
127
138
 
128
139
  def delete(self, name: str, token_type: QpuTokenTypeEnum, **kwargs) -> None:
129
- token: QpuTokenOut = self.get(name, token_type)
130
-
131
140
  response = self._client.delete(
132
- f"{self._get_endpoint_by_type(token_type)}/{token.id}", **kwargs
141
+ f"{self._get_endpoint_by_type(token_type)}/{name}", **kwargs
133
142
  )
134
143
  response.raise_for_status()
@@ -5,7 +5,6 @@ from typing import Any, Dict, List, Optional, Type, Union
5
5
 
6
6
  from pydantic import BaseModel, ValidationError
7
7
 
8
- from luna_sdk.exceptions.encryption_exception import EncryptionNotSetException
9
8
  from luna_sdk.interfaces.solutions_repo_i import ISolutionsRepo
10
9
  from luna_sdk.schemas.create.solution import SolutionIn
11
10
  from luna_sdk.schemas.enums.solution import SenseEnum
@@ -70,7 +69,6 @@ class SolutionsRepo(ISolutionsRepo):
70
69
  provider: str,
71
70
  qpu_tokens: Optional[TokenProvider] = None,
72
71
  solver_parameters: Optional[Union[Dict[str, Any], BaseModel]] = None,
73
- encryption_key: Optional[str] = None,
74
72
  name: Optional[str] = None,
75
73
  fail_on_invalid_params: bool = True,
76
74
  **kwargs,
@@ -85,9 +83,6 @@ class SolutionsRepo(ISolutionsRepo):
85
83
  if rest_qpu_tokens is None:
86
84
  rest_qpu_tokens = extract_qpu_tokens_from_env()
87
85
 
88
- encryption_key = encryption_key or os.environ.get("LUNA_ENCRYPTION_KEY")
89
- if encryption_key is None:
90
- raise EncryptionNotSetException
91
86
  params = SolutionsRepo.validate_solver_params(
92
87
  solver_name, provider, solver_parameters, fail_on_invalid_params
93
88
  )
@@ -98,7 +93,6 @@ class SolutionsRepo(ISolutionsRepo):
98
93
  provider=provider,
99
94
  parameters=params,
100
95
  qpu_tokens=rest_qpu_tokens,
101
- encryption_key=encryption_key,
102
96
  name=name,
103
97
  )
104
98
  response = self._client.post(
@@ -118,7 +112,6 @@ class SolutionsRepo(ISolutionsRepo):
118
112
  sleep_time_max: float = 60.0,
119
113
  sleep_time_increment: float = 5.0,
120
114
  sleep_time_initial: float = 5.0,
121
- encryption_key: Optional[str] = None,
122
115
  name: Optional[str] = None,
123
116
  fail_on_invalid_params: bool = True,
124
117
  **kwargs,
@@ -134,7 +127,6 @@ class SolutionsRepo(ISolutionsRepo):
134
127
  provider=provider,
135
128
  solver_parameters=params,
136
129
  qpu_tokens=qpu_tokens,
137
- encryption_key=encryption_key,
138
130
  name=name,
139
131
  **kwargs,
140
132
  )
@@ -18,12 +18,9 @@ class CircuitIn(BaseModel):
18
18
  The QASM circuit
19
19
  params: Dict[str, Any]
20
20
  Additional parameters
21
- encryption_key: str
22
- Encryption key to be used for encryption of QPU tokens.
23
21
  """
24
22
 
25
23
  provider: CircuitProviderEnum
26
24
  circuit: str
27
25
  params: Dict[str, Any] = {}
28
26
  qpu_tokens: Optional[RestAPITokenProvider] = None
29
- encryption_key: str
@@ -13,14 +13,11 @@ class QpuTokenIn(BaseModel):
13
13
  Name of provider
14
14
  token: str
15
15
  Token
16
- encryption_key: str
17
- Encryption key to be used for encryption of QPU tokens.
18
16
  """
19
17
 
20
18
  name: str
21
19
  provider: str
22
20
  token: str
23
- encryption_key: str
24
21
 
25
22
  class Config:
26
23
  extra = Extra.forbid
@@ -11,5 +11,4 @@ class SolutionIn(BaseModel):
11
11
  provider: str
12
12
  parameters: Dict[str, Any]
13
13
  qpu_tokens: Optional[RestAPITokenProvider] = None
14
- encryption_key: str
15
14
  name: Optional[str] = None
@@ -1,10 +1,11 @@
1
1
  from enum import Enum
2
2
 
3
3
 
4
- class InputType(str, Enum):
5
- bqm_spin = "bqm_spin"
6
- bqm_binary = "bqm_binary"
7
- cqm = "cqm"
8
- lp = "lp"
9
- aq = "aq"
10
- qubo = "qubo"
4
+ class OptFormat(str, Enum):
5
+ """Enumeration of all supported formats."""
6
+
7
+ AQ_MODEL = "AQ_MODEL"
8
+ LP = "LP"
9
+ QUBO = "QUBO_MATRIX"
10
+ CQM = "CQM"
11
+ BQM = "BQM"
@@ -2,5 +2,5 @@ from enum import Enum
2
2
 
3
3
 
4
4
  class QpuTokenTypeEnum(str, Enum):
5
- ORGANIZATION = "organization"
5
+ GROUP = "group"
6
6
  PERSONAL = "personal"
@@ -1,11 +1,12 @@
1
- from typing import Any, Dict, Generic, List, Optional, TypeVar
1
+ from typing import Any, Dict, Generic, Optional, TypeVar
2
2
 
3
3
  from pydantic import BaseModel, ConfigDict
4
4
 
5
- from luna_sdk.schemas.enums.optimization import InputType
5
+ from luna_sdk.schemas.enums.optimization import OptFormat
6
6
  from luna_sdk.schemas.optimization_formats.bqm import BQMSchema
7
7
  from luna_sdk.schemas.optimization_formats.cqm import CQMSchema
8
8
  from luna_sdk.schemas.optimization_formats.lp import LPSchema
9
+ from luna_sdk.schemas.optimization_formats.qubo import QuboSchema
9
10
  from luna_sdk.schemas.pretty_base import PrettyBase
10
11
  from luna_sdk.schemas.wrappers import PydanticDatetimeWrapper
11
12
 
@@ -33,22 +34,56 @@ class Optimization(PrettyBase):
33
34
  created_by: str
34
35
  modified_date: Optional[PydanticDatetimeWrapper] = None
35
36
  modified_by: Optional[str] = None
36
- input_type: Optional[InputType] = None
37
+ original_format: Optional[OptFormat] = None
37
38
  use_case_name: Optional[str] = None
38
39
  params: Optional[Dict[str, Any]] = None
39
40
 
40
- model_config = ConfigDict(extra="ignore", from_attributes=False)
41
-
42
-
43
- class OptimizationQubo(BaseModel):
44
- id: str
45
- name: Optional[str] = None
46
- created_date: PydanticDatetimeWrapper
47
- created_by: str
48
- modified_date: Optional[PydanticDatetimeWrapper] = None
49
- modified_by: Optional[str] = None
41
+ verbose: bool = False
42
+
43
+ def __str__(self):
44
+ if self.verbose:
45
+ return self.details()
46
+ return self.subset()
47
+
48
+ def details(self):
49
+ trimmed_keys = [
50
+ "verbose",
51
+ ]
52
+ data = self.model_dump()
53
+ output = ""
54
+ data_subset = {key: data[key] for key in data if key not in trimmed_keys}
55
+ ordered_subset = {
56
+ "id": data_subset.pop("id"),
57
+ "name": data_subset.pop("name"),
58
+ "original_format": data_subset.pop("original_format"),
59
+ **data_subset,
60
+ }
61
+ output += self._pretty_print(ordered_subset)
62
+ return output
63
+
64
+ def subset(self):
65
+ trimmed_keys = [
66
+ "created_by",
67
+ "modified_date",
68
+ "modified_by",
69
+ "use_case_name",
70
+ "params",
71
+ "verbose",
72
+ ]
73
+ data = self.model_dump()
74
+ output = ""
75
+ data_subset = {key: data[key] for key in data if key not in trimmed_keys}
76
+
77
+ ordered_subset = {
78
+ "id": data_subset.pop("id"),
79
+ "name": data_subset.pop("name"),
80
+ "original_format": data_subset.pop("original_format"),
81
+ **data_subset,
82
+ }
83
+ output += self._pretty_print(ordered_subset)
84
+ return output
50
85
 
51
- matrix: List[List[float]]
86
+ model_config = ConfigDict(extra="ignore", from_attributes=False)
52
87
 
53
88
 
54
89
  class OptimizationBQM(Optimization, BQMSchema): ...
@@ -60,10 +95,13 @@ class OptimizationCQM(Optimization, CQMSchema): ...
60
95
  class OptimizationLP(Optimization, LPSchema): ...
61
96
 
62
97
 
63
- class OptimizationUseCase(Optimization, BQMSchema):
98
+ class OptimizationUseCase(Optimization, QuboSchema):
64
99
  use_case: Dict[str, Any]
65
100
 
66
101
 
102
+ class OptimizationQubo(Optimization, QuboSchema): ...
103
+
104
+
67
105
  T = TypeVar("T")
68
106
 
69
107
 
@@ -0,0 +1,8 @@
1
+ from typing import List
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class QuboSchema(BaseModel):
7
+ matrix: List[List[float]]
8
+ offset: float
@@ -12,7 +12,7 @@ class QpuTokenSource(str, Enum):
12
12
  # stored token in user account
13
13
  PERSONAL = "personal"
14
14
  # stored token in group account
15
- ORGANIZATION = "organization"
15
+ GROUP = "group"
16
16
 
17
17
 
18
18
  class QpuToken(BaseModel):
@@ -44,16 +44,12 @@ class QpuTokenOut(BaseModel):
44
44
 
45
45
  Attributes
46
46
  ----------
47
- id: str
48
- Id of the QPU token
49
47
  name: Optional[str]
50
48
  Name of the QPU token
51
49
  provider: ProviderEnum
52
50
  Name of provider: dwave | ibm
53
51
  """
54
52
 
55
- id: str
56
-
57
53
  name: str
58
54
  provider: str
59
55
  token_type: QpuTokenTypeEnum
@@ -0,0 +1,18 @@
1
+ from enum import Enum
2
+
3
+
4
+ class _RESTQpuTokenSource(str, Enum):
5
+ """
6
+ This schema allow us not to change entire backend,
7
+ but just sync SDK and everything else in terms of qpu token source.
8
+ Currently, the difference is that
9
+ SDK has group qpu token source
10
+ and backend has organization qpu token source which are mapped to each other.
11
+ """
12
+
13
+ # token currently passed in from the API call (not stored by us)
14
+ INLINE = "inline"
15
+ # stored token in user account
16
+ PERSONAL = "personal"
17
+ # stored token in group account
18
+ ORGANIZATION = "organization"
@@ -1,20 +1,48 @@
1
- from typing import Optional
1
+ from typing import Optional, Union
2
2
 
3
3
  from pydantic import BaseModel, Extra
4
4
 
5
- from luna_sdk.schemas import QpuToken, TokenProvider
5
+ from luna_sdk.schemas import QpuToken, TokenProvider, QpuTokenSource
6
+ from luna_sdk.schemas.rest.qpu_token.qpu_token_source import _RESTQpuTokenSource
7
+
8
+
9
+ class _RestQpuToken(BaseModel):
10
+ source: _RESTQpuTokenSource
11
+ # A unique name for a stored token
12
+ name: Optional[str] = None
13
+ # This could be a QPU token, an API key or any token key for a QPU provider.
14
+ # If the token is not passed from this API call, one stored in the user's
15
+ # account will be used.
16
+ token: Optional[str] = None
17
+
18
+ @classmethod
19
+ def from_qpu_token(cls, qpu_token: Optional[QpuToken]) -> Optional["_RestQpuToken"]:
20
+ if qpu_token is None:
21
+ return None
22
+ # Organizational tokens were renamed to group in #1851
23
+ # For smoother transition we only change naming in the SDK,
24
+ # and therefore we need a mapping between Group and Organization here.
25
+ # However, in backend for now QPU tokens still has source organization
26
+ # TODO: Remove it when backend I/O schema is changed
27
+ if qpu_token.source == QpuTokenSource.GROUP:
28
+ return cls(
29
+ source=_RESTQpuTokenSource.ORGANIZATION,
30
+ name=qpu_token.name,
31
+ token=qpu_token.token,
32
+ )
33
+ return cls.model_validate(qpu_token, from_attributes=True)
6
34
 
7
35
 
8
36
  class AWSQpuTokens(BaseModel):
9
- aws_access_key: QpuToken
10
- aws_secret_access_key: QpuToken
37
+ aws_access_key: _RestQpuToken
38
+ aws_secret_access_key: _RestQpuToken
11
39
 
12
40
 
13
41
  class RestAPITokenProvider(BaseModel):
14
- dwave: Optional[QpuToken] = None
15
- ibm: Optional[QpuToken] = None
16
- fujitsu: Optional[QpuToken] = None
17
- qctrl: Optional[QpuToken] = None
42
+ dwave: Optional[_RestQpuToken] = None
43
+ ibm: Optional[_RestQpuToken] = None
44
+ fujitsu: Optional[_RestQpuToken] = None
45
+ qctrl: Optional[_RestQpuToken] = None
18
46
  aws: Optional[AWSQpuTokens] = None
19
47
 
20
48
  @classmethod
@@ -28,16 +56,20 @@ class RestAPITokenProvider(BaseModel):
28
56
  ):
29
57
  # Ignoring mypy here to receive validation error, because we always need 2 tokens for aws
30
58
  aws = AWSQpuTokens(
31
- aws_access_key=getattr(token_provider, "aws_access_key", None), # type: ignore[arg-type]
32
- aws_secret_access_key=getattr( # type: ignore[arg-type]
33
- token_provider, "aws_secret_access_key", None
59
+ aws_access_key=_RestQpuToken.from_qpu_token(
60
+ getattr(token_provider, "aws_access_key", None)
61
+ ), # type: ignore[arg-type]
62
+ aws_secret_access_key=_RestQpuToken.from_qpu_token(
63
+ getattr( # type: ignore[arg-type]
64
+ token_provider, "aws_secret_access_key", None
65
+ )
34
66
  ),
35
67
  )
36
68
  return cls(
37
- dwave=token_provider.dwave,
38
- ibm=token_provider.ibm,
39
- fujitsu=token_provider.fujitsu,
40
- qctrl=token_provider.qctrl,
69
+ dwave=_RestQpuToken.from_qpu_token(token_provider.dwave),
70
+ ibm=_RestQpuToken.from_qpu_token(token_provider.ibm),
71
+ fujitsu=_RestQpuToken.from_qpu_token(token_provider.fujitsu),
72
+ qctrl=_RestQpuToken.from_qpu_token(token_provider.qctrl),
41
73
  aws=aws,
42
74
  )
43
75