boulder-opal-scale-up-sdk 1.0.0__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.
- boulder_opal_scale_up_sdk-1.0.0.dist-info/METADATA +38 -0
- boulder_opal_scale_up_sdk-1.0.0.dist-info/RECORD +50 -0
- boulder_opal_scale_up_sdk-1.0.0.dist-info/WHEEL +4 -0
- boulderopalscaleupsdk/__init__.py +14 -0
- boulderopalscaleupsdk/agent/__init__.py +29 -0
- boulderopalscaleupsdk/agent/worker.py +244 -0
- boulderopalscaleupsdk/common/__init__.py +12 -0
- boulderopalscaleupsdk/common/dtypes.py +353 -0
- boulderopalscaleupsdk/common/typeclasses.py +85 -0
- boulderopalscaleupsdk/device/__init__.py +16 -0
- boulderopalscaleupsdk/device/common.py +58 -0
- boulderopalscaleupsdk/device/config_loader.py +88 -0
- boulderopalscaleupsdk/device/controller/__init__.py +32 -0
- boulderopalscaleupsdk/device/controller/base.py +18 -0
- boulderopalscaleupsdk/device/controller/qblox.py +664 -0
- boulderopalscaleupsdk/device/controller/quantum_machines.py +139 -0
- boulderopalscaleupsdk/device/device.py +35 -0
- boulderopalscaleupsdk/device/processor/__init__.py +23 -0
- boulderopalscaleupsdk/device/processor/common.py +148 -0
- boulderopalscaleupsdk/device/processor/superconducting_processor.py +291 -0
- boulderopalscaleupsdk/experiments/__init__.py +44 -0
- boulderopalscaleupsdk/experiments/common.py +96 -0
- boulderopalscaleupsdk/experiments/power_rabi.py +60 -0
- boulderopalscaleupsdk/experiments/ramsey.py +55 -0
- boulderopalscaleupsdk/experiments/resonator_spectroscopy.py +64 -0
- boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py +76 -0
- boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py +64 -0
- boulderopalscaleupsdk/grpc_interceptors/__init__.py +16 -0
- boulderopalscaleupsdk/grpc_interceptors/auth.py +101 -0
- boulderopalscaleupsdk/plotting/__init__.py +24 -0
- boulderopalscaleupsdk/plotting/dtypes.py +221 -0
- boulderopalscaleupsdk/protobuf/v1/agent_pb2.py +48 -0
- boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi +53 -0
- boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py +138 -0
- boulderopalscaleupsdk/protobuf/v1/device_pb2.py +71 -0
- boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi +110 -0
- boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py +274 -0
- boulderopalscaleupsdk/protobuf/v1/task_pb2.py +53 -0
- boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi +118 -0
- boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py +119 -0
- boulderopalscaleupsdk/py.typed +0 -0
- boulderopalscaleupsdk/routines/__init__.py +9 -0
- boulderopalscaleupsdk/routines/common.py +10 -0
- boulderopalscaleupsdk/routines/resonator_mapping.py +13 -0
- boulderopalscaleupsdk/third_party/__init__.py +14 -0
- boulderopalscaleupsdk/third_party/quantum_machines/__init__.py +51 -0
- boulderopalscaleupsdk/third_party/quantum_machines/config.py +597 -0
- boulderopalscaleupsdk/third_party/quantum_machines/constants.py +20 -0
- boulderopalscaleupsdk/utils/__init__.py +12 -0
- boulderopalscaleupsdk/utils/serial_utils.py +62 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: boulder-opal-scale-up-sdk
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: Q-CTRL Boulder Opal Scale Up Python SDK
|
5
|
+
License: Apache-2.0
|
6
|
+
Keywords: black opal,boulder opal,fire opal,nisq,open controls,q control,q ctrl,q-control,q-ctrl,qcontrol,qctrl,quantum,quantum algorithms,quantum circuits,quantum coding,quantum coding software,quantum computing,quantum control,quantum control software,quantum control theory,quantum engineering,quantum error correction,quantum firmware,quantum fundamentals,quantum sensing,qubit,qudit
|
7
|
+
Author: Q-CTRL
|
8
|
+
Author-email: support@q-ctrl.com
|
9
|
+
Maintainer: Q-CTRL
|
10
|
+
Maintainer-email: support@q-ctrl.com
|
11
|
+
Requires-Python: >=3.10,<3.13
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
13
|
+
Classifier: Environment :: Console
|
14
|
+
Classifier: Intended Audience :: Developers
|
15
|
+
Classifier: Intended Audience :: Education
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
17
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
18
|
+
Classifier: Natural Language :: English
|
19
|
+
Classifier: Operating System :: OS Independent
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
27
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
28
|
+
Classifier: Topic :: System :: Distributed Computing
|
29
|
+
Requires-Dist: async-timeout (>=4.0.3,<5.0.0) ; python_version < "3.11"
|
30
|
+
Requires-Dist: attrs (>=25.1.0,<26.0.0)
|
31
|
+
Requires-Dist: googleapis-common-protos (>=1.69.2,<2.0.0)
|
32
|
+
Requires-Dist: qctrl-client (>=10.1.0,<11.0.0)
|
33
|
+
Project-URL: Facebook, https://www.facebook.com/qctrl
|
34
|
+
Project-URL: GitHub, https://github.com/qctrl
|
35
|
+
Project-URL: Homepage, https://q-ctrl.com
|
36
|
+
Project-URL: LinkedIn, https://www.linkedin.com/company/q-ctrl/
|
37
|
+
Project-URL: X, https://x.com/qctrlHQ
|
38
|
+
Project-URL: YouTube, https://www.youtube.com/qctrl
|
@@ -0,0 +1,50 @@
|
|
1
|
+
boulderopalscaleupsdk/__init__.py,sha256=nD3YDqPiE52mmuUrIlDUrYSyljpMsDJvc5HsubBUSs4,592
|
2
|
+
boulderopalscaleupsdk/agent/__init__.py,sha256=aFkAtHJDOdXA126JklxYz0ix1k4lCcLLS9DQp8zUKMk,1092
|
3
|
+
boulderopalscaleupsdk/agent/worker.py,sha256=qoGmYBqrDTiifer2W_mFz8sR5cYcUJ5-oTvALPtvCVo,7640
|
4
|
+
boulderopalscaleupsdk/common/__init__.py,sha256=vLaJ1FP36xV5eX6VD7nShqxVBm3UzsBMIK1pmSiTIag,550
|
5
|
+
boulderopalscaleupsdk/common/dtypes.py,sha256=LGA6i1AioWS2yvtXnlbrgc2-iJLVY0RkAHzTxOUF6uY,11219
|
6
|
+
boulderopalscaleupsdk/common/typeclasses.py,sha256=FZHyTcuCQH5niqFPZmIyoO2zN6mA661h8kyvDcYFryg,3033
|
7
|
+
boulderopalscaleupsdk/device/__init__.py,sha256=47Esmn8cdjVFOIHCeI9mEYYKwqM_K_sESFimj_D18iA,710
|
8
|
+
boulderopalscaleupsdk/device/common.py,sha256=ZLXpmORKQYntX7DeKZ8pd49Ju1jzidR2q2EeN3TNwTI,1909
|
9
|
+
boulderopalscaleupsdk/device/config_loader.py,sha256=AvMNH4J2SpRGdtyWglJXNHJestGHFQdAM8DmUh_TotQ,3300
|
10
|
+
boulderopalscaleupsdk/device/controller/__init__.py,sha256=gkjhvarEIE7cLnwxgl2vuKG6ddpJtgJCr2E7T_yRd5s,935
|
11
|
+
boulderopalscaleupsdk/device/controller/base.py,sha256=BbQN26ehLIHgLqqwZpn7QhDzMxogQz0gXH5yK0J_cb0,653
|
12
|
+
boulderopalscaleupsdk/device/controller/qblox.py,sha256=pzR1oS4Ycxs8VowJhMVnbEWS_31u8bX-6yeHwL83VqM,21725
|
13
|
+
boulderopalscaleupsdk/device/controller/quantum_machines.py,sha256=kf9cvuig37qCfrk_82UsIR7V09wskAE9fYNb68DBZcU,5014
|
14
|
+
boulderopalscaleupsdk/device/device.py,sha256=ze_27Q97Kp2YxW9qiqXMnax02KZgcOjmekJpOHHquSA,1118
|
15
|
+
boulderopalscaleupsdk/device/processor/__init__.py,sha256=uVmeVHunBNLeQj5YjQTaR0Gf2zoXcSjtEv2t-zD-zxs,919
|
16
|
+
boulderopalscaleupsdk/device/processor/common.py,sha256=hU2g0UaXZhmycCChMazDLSYYCnqO0RsApFd5CmhXS0g,5397
|
17
|
+
boulderopalscaleupsdk/device/processor/superconducting_processor.py,sha256=J5fUZfn2Q-dI8qbgO_3gm01HaGVg3E1U50ha0PbsczU,10297
|
18
|
+
boulderopalscaleupsdk/experiments/__init__.py,sha256=Np_80a-oOWoGBA1BO6od8yUoxz7WP-9CmPpRGzbTm50,1297
|
19
|
+
boulderopalscaleupsdk/experiments/common.py,sha256=nxcXx0XJbsGQx7t899wXbxCOyJZwFTMl4cvzjosyAJ4,2381
|
20
|
+
boulderopalscaleupsdk/experiments/power_rabi.py,sha256=k96FAtF0jGjzd3x2orsQZt2T9IymKAx5pX-rgMVAwPE,2048
|
21
|
+
boulderopalscaleupsdk/experiments/ramsey.py,sha256=DD0Y3aRM_iVXKFQSDbCfPoZ-uo8yEw4G-H7bh5MltbE,1747
|
22
|
+
boulderopalscaleupsdk/experiments/resonator_spectroscopy.py,sha256=CC8kD-Ph5RlUzVTNdtzTYirrYyBi1whq5Gj5ZZO79j4,2177
|
23
|
+
boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py,sha256=9NEOCss1J0X8FXRxFlb6J0s0aQh-zq5v8tPPX7SlDJE,2736
|
24
|
+
boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py,sha256=OHbN9nKzmJmIHU4S5MdmELjLDu0nTEgNksUgnEUzNpo,2211
|
25
|
+
boulderopalscaleupsdk/grpc_interceptors/__init__.py,sha256=PkdOlllLbRWycofvGCCxDY60Ydp_U0QM5l4u3Fsnno4,616
|
26
|
+
boulderopalscaleupsdk/grpc_interceptors/auth.py,sha256=E8x8Q_s2y2ODNEU1hYPUEvkXP1qF_BOdWJh5L7_85TU,4066
|
27
|
+
boulderopalscaleupsdk/plotting/__init__.py,sha256=BnEBs59gMIjHHtmowah8BuUTTfHAakqlmYdbzYSvcyg,730
|
28
|
+
boulderopalscaleupsdk/plotting/dtypes.py,sha256=uEbvLiTUXgXHbIsaLw_qQy8iPX3QXdIZaI-TRFPRE8w,7098
|
29
|
+
boulderopalscaleupsdk/protobuf/v1/agent_pb2.py,sha256=T8s1pJWsCkNgUwt4cUHMjf5j6YvyCcsFrLPKW5pJdpA,5304
|
30
|
+
boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi,sha256=hFbkqtvhDi07sKaml-oYP7UTmlOuuqt3xXas8U_9AKA,2309
|
31
|
+
boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py,sha256=UegXJdsZ0eEKgAzw-bIKf9CH4tdN6bo0Qx4ekd7t6UA,6914
|
32
|
+
boulderopalscaleupsdk/protobuf/v1/device_pb2.py,sha256=Upx7_CwiUvN7xH18kx8A5HtVoC4zOsTizah8ytnlN1s,8603
|
33
|
+
boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi,sha256=8WiyZW8I8Z6SOgBbRLlcICR2gWzD_d5n3xju4GWq26U,4880
|
34
|
+
boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py,sha256=WLesWpFrqs6woRYOYZC1dWmJ2Nl38DvBxQ1ob6n-gAA,13747
|
35
|
+
boulderopalscaleupsdk/protobuf/v1/task_pb2.py,sha256=7NWFFBEPij8B9dvexRia3B_T9-oOCDwayASCglvBtO4,7086
|
36
|
+
boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi,sha256=QWciGKFD1AcaAhqgym0ZwNtIlNT85HhOTBdivEC6QJE,5952
|
37
|
+
boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py,sha256=yP2FZ148RmSYFHivatCXuM13oRBDA5wBwmvhZQNBTXQ,5826
|
38
|
+
boulderopalscaleupsdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
|
+
boulderopalscaleupsdk/routines/__init__.py,sha256=cowyGcDR2ln2nDAguw6JmilSRxwFhGis2Kg0plT82r8,154
|
40
|
+
boulderopalscaleupsdk/routines/common.py,sha256=Lav4eH4GJyRJXBZwmhA3b34UTgck5x4URex3SBSctwM,224
|
41
|
+
boulderopalscaleupsdk/routines/resonator_mapping.py,sha256=nbbnAaImH4Bpjw67PSdlEX8SY7_2-KRoVHk1NQCDUr8,284
|
42
|
+
boulderopalscaleupsdk/third_party/__init__.py,sha256=b9T2IVfz9bMenSsLQuaf4A14ySinjNnSGx68hLsFHZE,596
|
43
|
+
boulderopalscaleupsdk/third_party/quantum_machines/__init__.py,sha256=oYtqq0Iw2tGxnXZ2kkUmVhNplRD0StlP-jw168sH3BU,1565
|
44
|
+
boulderopalscaleupsdk/third_party/quantum_machines/config.py,sha256=eJFs1MGaThIC7AR-iz8SDCEZssf7io_cvn0wUjL8Qrk,19912
|
45
|
+
boulderopalscaleupsdk/third_party/quantum_machines/constants.py,sha256=5PpAi6MZ53yEinBHerY2ssi30BR37JnLBWL21irpZ8Y,870
|
46
|
+
boulderopalscaleupsdk/utils/__init__.py,sha256=vLaJ1FP36xV5eX6VD7nShqxVBm3UzsBMIK1pmSiTIag,550
|
47
|
+
boulderopalscaleupsdk/utils/serial_utils.py,sha256=SzvY-WFD0b8IGSh6PTv7F9y1W2IPPYmj8pY3AsX-xlM,2062
|
48
|
+
boulder_opal_scale_up_sdk-1.0.0.dist-info/METADATA,sha256=hsUMkexqKKXYkipWU07rqqOSUL-wLBB8mUrw9oqb7d8,2025
|
49
|
+
boulder_opal_scale_up_sdk-1.0.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
50
|
+
boulder_opal_scale_up_sdk-1.0.0.dist-info/RECORD,,
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Copyright 2025 Q-CTRL. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Q-CTRL Terms of service (the "License"). Unauthorized
|
4
|
+
# copying or use of this file, via any medium, is strictly prohibited.
|
5
|
+
# Proprietary and confidential. You may not use this file except in compliance
|
6
|
+
# with the License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# https://q-ctrl.com/terms
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
|
+
# License for the specific language.
|
13
|
+
|
14
|
+
"""Boulder Opal Scale up SDK package."""
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright 2025 Q-CTRL. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Q-CTRL Terms of service (the "License"). Unauthorized
|
4
|
+
# copying or use of this file, via any medium, is strictly prohibited.
|
5
|
+
# Proprietary and confidential. You may not use this file except in compliance
|
6
|
+
# with the License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# https://q-ctrl.com/terms
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
|
+
# License for the specific language.
|
13
|
+
|
14
|
+
"""Agent module.
|
15
|
+
|
16
|
+
Agents are responsible for communicating with Q-CTRL APIs to determine a set of tasks to
|
17
|
+
perform (generally, QPU tasks such as execution of quantum circuits / configuration).
|
18
|
+
Importantly, to both simplify and ensure the security of the QPU adjacent host that the
|
19
|
+
agent will run on, the agent must initiate all communications (as opposed to opening a
|
20
|
+
port on the host).
|
21
|
+
"""
|
22
|
+
|
23
|
+
__all__ = [
|
24
|
+
"Agent",
|
25
|
+
"AgentSettings",
|
26
|
+
"TaskHandler",
|
27
|
+
]
|
28
|
+
|
29
|
+
from boulderopalscaleupsdk.agent.worker import Agent, AgentSettings, TaskHandler
|
@@ -0,0 +1,244 @@
|
|
1
|
+
# Copyright 2025 Q-CTRL. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Q-CTRL Terms of service (the "License"). Unauthorized
|
4
|
+
# copying or use of this file, via any medium, is strictly prohibited.
|
5
|
+
# Proprietary and confidential. You may not use this file except in compliance
|
6
|
+
# with the License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# https://q-ctrl.com/terms
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
|
+
# License for the specific language.
|
13
|
+
|
14
|
+
"""Base agent module.
|
15
|
+
|
16
|
+
Contains logic for agent worker.
|
17
|
+
"""
|
18
|
+
|
19
|
+
import logging
|
20
|
+
from abc import abstractmethod
|
21
|
+
from typing import Protocol
|
22
|
+
|
23
|
+
from google.protobuf import any_pb2
|
24
|
+
from google.protobuf.message import Message
|
25
|
+
from google.protobuf.struct_pb2 import Struct
|
26
|
+
from grpc import aio as grpc
|
27
|
+
from grpc import ssl_channel_credentials
|
28
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
29
|
+
|
30
|
+
from boulderopalscaleupsdk.protobuf.v1 import agent_pb2, task_pb2, task_pb2_grpc
|
31
|
+
|
32
|
+
LOG = logging.getLogger(__name__)
|
33
|
+
|
34
|
+
|
35
|
+
class AgentSettings(BaseSettings):
|
36
|
+
"""Configuration for the Agent."""
|
37
|
+
|
38
|
+
model_config = SettingsConfigDict(
|
39
|
+
env_file=".env",
|
40
|
+
env_prefix="QCTRL_SCALE_UP_",
|
41
|
+
env_file_encoding="utf-8",
|
42
|
+
env_nested_delimiter="__",
|
43
|
+
extra="ignore",
|
44
|
+
)
|
45
|
+
|
46
|
+
agent_id: str
|
47
|
+
remote_url: str
|
48
|
+
|
49
|
+
|
50
|
+
class TaskHandler(Protocol):
|
51
|
+
@abstractmethod
|
52
|
+
async def handle(
|
53
|
+
self,
|
54
|
+
request: agent_pb2.RunQuaProgramRequest
|
55
|
+
| agent_pb2.RunQuantumMachinesMixerCalibrationRequest
|
56
|
+
| agent_pb2.DisplayResultsRequest,
|
57
|
+
) -> (
|
58
|
+
agent_pb2.RunQuaProgramResponse
|
59
|
+
| agent_pb2.RunQuantumMachinesMixerCalibrationResponse
|
60
|
+
| agent_pb2.DisplayResultsResponse
|
61
|
+
| task_pb2.TaskErrorDetail
|
62
|
+
): ...
|
63
|
+
|
64
|
+
async def run(
|
65
|
+
self,
|
66
|
+
task: task_pb2.Task,
|
67
|
+
) -> any_pb2.Any | task_pb2.TaskErrorDetail:
|
68
|
+
request = (
|
69
|
+
_as_run_qua_program_request(
|
70
|
+
task.data,
|
71
|
+
)
|
72
|
+
or _as_run_qua_calibration_request(task.data)
|
73
|
+
or _as_display_results_request(task.data)
|
74
|
+
)
|
75
|
+
match request:
|
76
|
+
case (
|
77
|
+
agent_pb2.RunQuaProgramRequest()
|
78
|
+
| agent_pb2.RunQuantumMachinesMixerCalibrationRequest()
|
79
|
+
| agent_pb2.DisplayResultsRequest()
|
80
|
+
):
|
81
|
+
return _as_any_message(await self.handle(request))
|
82
|
+
case None:
|
83
|
+
return task_pb2.TaskErrorDetail(
|
84
|
+
code=task_pb2.TaskError.TASK_ERROR_NOT_IMPLEMENTED,
|
85
|
+
detail="Unknown or unrecognized request.",
|
86
|
+
)
|
87
|
+
|
88
|
+
|
89
|
+
def _as_any_message(message: Message) -> any_pb2.Any:
|
90
|
+
msg = any_pb2.Any()
|
91
|
+
msg.Pack(message) # type: ignore[reportUnknownMemberType]
|
92
|
+
return msg
|
93
|
+
|
94
|
+
|
95
|
+
def _as_run_qua_program_request(
|
96
|
+
task_result: any_pb2.Any,
|
97
|
+
) -> agent_pb2.RunQuaProgramRequest | None:
|
98
|
+
request = agent_pb2.RunQuaProgramRequest()
|
99
|
+
unpacked: bool = task_result.Unpack(request) # type: ignore[reportUnknownMemberType]
|
100
|
+
if not unpacked:
|
101
|
+
return None
|
102
|
+
|
103
|
+
return request
|
104
|
+
|
105
|
+
|
106
|
+
def _as_run_qua_calibration_request(
|
107
|
+
task_result: any_pb2.Any,
|
108
|
+
) -> agent_pb2.RunQuantumMachinesMixerCalibrationRequest | None:
|
109
|
+
request = agent_pb2.RunQuantumMachinesMixerCalibrationRequest()
|
110
|
+
unpacked: bool = task_result.Unpack(request) # type: ignore[reportUnknownMemberType]
|
111
|
+
if not unpacked:
|
112
|
+
return None
|
113
|
+
|
114
|
+
return request
|
115
|
+
|
116
|
+
|
117
|
+
def _as_display_results_request(
|
118
|
+
task_result: any_pb2.Any,
|
119
|
+
) -> agent_pb2.DisplayResultsRequest | None:
|
120
|
+
request = agent_pb2.DisplayResultsRequest()
|
121
|
+
unpacked: bool = task_result.Unpack(request) # type: ignore[reportUnknownMemberType]
|
122
|
+
if not unpacked:
|
123
|
+
return None
|
124
|
+
|
125
|
+
return request
|
126
|
+
|
127
|
+
|
128
|
+
class Agent:
|
129
|
+
"""Agent implementation."""
|
130
|
+
|
131
|
+
def __init__(
|
132
|
+
self,
|
133
|
+
agent_settings: AgentSettings,
|
134
|
+
handler: TaskHandler,
|
135
|
+
grpc_interceptors: list[grpc.ClientInterceptor] | None = None,
|
136
|
+
) -> None:
|
137
|
+
self._settings = agent_settings
|
138
|
+
self._handler = handler
|
139
|
+
self._channel: grpc.Channel | None = self._create_channel(
|
140
|
+
agent_settings.remote_url,
|
141
|
+
grpc_interceptors,
|
142
|
+
)
|
143
|
+
self._agent_manager = task_pb2_grpc.AgentManagerServiceStub(self._channel)
|
144
|
+
self._state: task_pb2.AgentState = task_pb2.AGENT_STATE_ACTIVE_IDLE
|
145
|
+
|
146
|
+
@property
|
147
|
+
def agent_id(self) -> str:
|
148
|
+
"""The agent identifier."""
|
149
|
+
return self._settings.agent_id
|
150
|
+
|
151
|
+
def _create_channel(
|
152
|
+
self,
|
153
|
+
url: str,
|
154
|
+
interceptors: list[grpc.ClientInterceptor] | None = None,
|
155
|
+
) -> grpc.Channel:
|
156
|
+
"""
|
157
|
+
Create a gRPC channel.
|
158
|
+
"""
|
159
|
+
host = url.split(":")[0]
|
160
|
+
if host in ["localhost", "127.0.0.1", "0.0.0.0", "::"]:
|
161
|
+
channel = grpc.insecure_channel(url)
|
162
|
+
else:
|
163
|
+
channel = grpc.secure_channel(
|
164
|
+
url,
|
165
|
+
credentials=ssl_channel_credentials(),
|
166
|
+
interceptors=interceptors,
|
167
|
+
)
|
168
|
+
return channel
|
169
|
+
|
170
|
+
async def start_session(
|
171
|
+
self,
|
172
|
+
app: str,
|
173
|
+
device_name: str,
|
174
|
+
routine: str,
|
175
|
+
data: Struct | None,
|
176
|
+
) -> str:
|
177
|
+
if not self._channel:
|
178
|
+
raise RuntimeError("Agent is shutdown.")
|
179
|
+
_data = any_pb2.Any()
|
180
|
+
if data:
|
181
|
+
_data.Pack(data)
|
182
|
+
|
183
|
+
response: task_pb2.AgentTasksResponse = await self._agent_manager.StartSession(
|
184
|
+
task_pb2.StartSessionRequest(
|
185
|
+
agent_id=self.agent_id,
|
186
|
+
app_name=app,
|
187
|
+
device_name=device_name,
|
188
|
+
routine_name=routine,
|
189
|
+
data=_data,
|
190
|
+
),
|
191
|
+
)
|
192
|
+
self._state = response.target_state
|
193
|
+
await self._resume(response)
|
194
|
+
await self.shutdown()
|
195
|
+
return response.session_id
|
196
|
+
|
197
|
+
async def _resume(self, response: task_pb2.AgentTasksResponse) -> None:
|
198
|
+
tasks = response.tasks
|
199
|
+
while self._state not in [
|
200
|
+
task_pb2.AGENT_STATE_SHUTDOWN_MANAGER_INITIATED,
|
201
|
+
task_pb2.AGENT_STATE_SHUTDOWN_FAULT,
|
202
|
+
task_pb2.AGENT_STATE_SHUTDOWN_CLIENT_INITIATED,
|
203
|
+
]:
|
204
|
+
task_results: list[task_pb2.TaskResult] = []
|
205
|
+
for task in tasks:
|
206
|
+
err: task_pb2.TaskErrorDetail | None = None
|
207
|
+
result: any_pb2.Any | None = None
|
208
|
+
|
209
|
+
match await self._handler.run(task):
|
210
|
+
case task_pb2.TaskErrorDetail() as err:
|
211
|
+
pass
|
212
|
+
case any_pb2.Any() as result:
|
213
|
+
pass
|
214
|
+
|
215
|
+
task_results.append(
|
216
|
+
task_pb2.TaskResult(
|
217
|
+
task_id=task.task_id,
|
218
|
+
session_id=task.session_id,
|
219
|
+
result=result,
|
220
|
+
error=err,
|
221
|
+
),
|
222
|
+
)
|
223
|
+
|
224
|
+
_resp: task_pb2.AgentTasksResponse = await self._agent_manager.UpdateSession(
|
225
|
+
task_pb2.UpdateSessionRequest(
|
226
|
+
session_id=response.session_id,
|
227
|
+
current_state=self._state,
|
228
|
+
results=task_results,
|
229
|
+
task_in_progress=[],
|
230
|
+
),
|
231
|
+
)
|
232
|
+
|
233
|
+
tasks = _resp.tasks
|
234
|
+
self._state = _resp.target_state
|
235
|
+
|
236
|
+
async def shutdown(
|
237
|
+
self,
|
238
|
+
timeout: float | None = None,
|
239
|
+
_: BaseException | None = None,
|
240
|
+
) -> None:
|
241
|
+
"""Shutdown the agent."""
|
242
|
+
if self._channel:
|
243
|
+
await self._channel.close(grace=timeout)
|
244
|
+
self._channel = None
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Copyright 2025 Q-CTRL. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Q-CTRL Terms of service (the "License"). Unauthorized
|
4
|
+
# copying or use of this file, via any medium, is strictly prohibited.
|
5
|
+
# Proprietary and confidential. You may not use this file except in compliance
|
6
|
+
# with the License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# https://q-ctrl.com/terms
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
|
+
# License for the specific language.
|