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.
Files changed (50) hide show
  1. boulder_opal_scale_up_sdk-1.0.0.dist-info/METADATA +38 -0
  2. boulder_opal_scale_up_sdk-1.0.0.dist-info/RECORD +50 -0
  3. boulder_opal_scale_up_sdk-1.0.0.dist-info/WHEEL +4 -0
  4. boulderopalscaleupsdk/__init__.py +14 -0
  5. boulderopalscaleupsdk/agent/__init__.py +29 -0
  6. boulderopalscaleupsdk/agent/worker.py +244 -0
  7. boulderopalscaleupsdk/common/__init__.py +12 -0
  8. boulderopalscaleupsdk/common/dtypes.py +353 -0
  9. boulderopalscaleupsdk/common/typeclasses.py +85 -0
  10. boulderopalscaleupsdk/device/__init__.py +16 -0
  11. boulderopalscaleupsdk/device/common.py +58 -0
  12. boulderopalscaleupsdk/device/config_loader.py +88 -0
  13. boulderopalscaleupsdk/device/controller/__init__.py +32 -0
  14. boulderopalscaleupsdk/device/controller/base.py +18 -0
  15. boulderopalscaleupsdk/device/controller/qblox.py +664 -0
  16. boulderopalscaleupsdk/device/controller/quantum_machines.py +139 -0
  17. boulderopalscaleupsdk/device/device.py +35 -0
  18. boulderopalscaleupsdk/device/processor/__init__.py +23 -0
  19. boulderopalscaleupsdk/device/processor/common.py +148 -0
  20. boulderopalscaleupsdk/device/processor/superconducting_processor.py +291 -0
  21. boulderopalscaleupsdk/experiments/__init__.py +44 -0
  22. boulderopalscaleupsdk/experiments/common.py +96 -0
  23. boulderopalscaleupsdk/experiments/power_rabi.py +60 -0
  24. boulderopalscaleupsdk/experiments/ramsey.py +55 -0
  25. boulderopalscaleupsdk/experiments/resonator_spectroscopy.py +64 -0
  26. boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py +76 -0
  27. boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py +64 -0
  28. boulderopalscaleupsdk/grpc_interceptors/__init__.py +16 -0
  29. boulderopalscaleupsdk/grpc_interceptors/auth.py +101 -0
  30. boulderopalscaleupsdk/plotting/__init__.py +24 -0
  31. boulderopalscaleupsdk/plotting/dtypes.py +221 -0
  32. boulderopalscaleupsdk/protobuf/v1/agent_pb2.py +48 -0
  33. boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi +53 -0
  34. boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py +138 -0
  35. boulderopalscaleupsdk/protobuf/v1/device_pb2.py +71 -0
  36. boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi +110 -0
  37. boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py +274 -0
  38. boulderopalscaleupsdk/protobuf/v1/task_pb2.py +53 -0
  39. boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi +118 -0
  40. boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py +119 -0
  41. boulderopalscaleupsdk/py.typed +0 -0
  42. boulderopalscaleupsdk/routines/__init__.py +9 -0
  43. boulderopalscaleupsdk/routines/common.py +10 -0
  44. boulderopalscaleupsdk/routines/resonator_mapping.py +13 -0
  45. boulderopalscaleupsdk/third_party/__init__.py +14 -0
  46. boulderopalscaleupsdk/third_party/quantum_machines/__init__.py +51 -0
  47. boulderopalscaleupsdk/third_party/quantum_machines/config.py +597 -0
  48. boulderopalscaleupsdk/third_party/quantum_machines/constants.py +20 -0
  49. boulderopalscaleupsdk/utils/__init__.py +12 -0
  50. 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,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.0.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -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.