Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add custom url resolver to enable using other endpoints #1914

Merged
merged 2 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions qiskit_ibm_runtime/api/client_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

"""Represent IBM Quantum account client parameters."""

from typing import Dict, Optional, Any, Union
from typing import Dict, Optional, Any, Union, Callable
from ..proxies import ProxyConfiguration

from ..utils import get_runtime_api_base_url
from ..utils import default_runtime_url_resolver
from ..api.auth import QuantumAuth, CloudAuth

TEMPLATE_IBM_HUBS = "{prefix}/Network/{hub}/Groups/{group}/Projects/{project}"
Expand All @@ -34,6 +34,7 @@ def __init__(
proxies: Optional[ProxyConfiguration] = None,
verify: bool = True,
private_endpoint: Optional[bool] = False,
url_resolver: Optional[Callable[[str, str, Optional[bool]], str]] = None,
) -> None:
"""ClientParameters constructor.
Expand All @@ -45,6 +46,7 @@ def __init__(
proxies: Proxy configuration.
verify: If ``False``, ignores SSL certificates errors.
private_endpoint: Connect to private API URL.
url_resolver: Function used to resolve the runtime url.
"""
self.token = token
self.instance = instance
Expand All @@ -53,6 +55,9 @@ def __init__(
self.proxies = proxies
self.verify = verify
self.private_endpoint = private_endpoint
if not url_resolver:
url_resolver = default_runtime_url_resolver
self.url_resolver = url_resolver

def get_auth_handler(self) -> Union[CloudAuth, QuantumAuth]:
"""Returns the respective authentication handler."""
Expand All @@ -63,7 +68,7 @@ def get_auth_handler(self) -> Union[CloudAuth, QuantumAuth]:

def get_runtime_api_base_url(self) -> str:
"""Returns the Runtime API base url."""
return get_runtime_api_base_url(self.url, self.instance, self.private_endpoint)
return self.url_resolver(self.url, self.instance, self.private_endpoint)

def connection_parameters(self) -> Dict[str, Any]:
"""Construct connection related parameters.
Expand Down
5 changes: 5 additions & 0 deletions qiskit_ibm_runtime/qiskit_runtime_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def __init__(
verify: Optional[bool] = None,
channel_strategy: Optional[str] = None,
private_endpoint: Optional[bool] = None,
url_resolver: Optional[Callable[[str, str, Optional[bool]], str]] = None,
) -> None:
"""QiskitRuntimeService constructor
Expand Down Expand Up @@ -117,6 +118,7 @@ def __init__(
verify: Whether to verify the server's TLS certificate.
channel_strategy: Error mitigation strategy.
private_endpoint: Connect to private API URL.
url_resolver: Function used to resolve the runtime url.
Returns:
An instance of QiskitRuntimeService or QiskitRuntimeLocalService for local channel.
Expand Down Expand Up @@ -149,11 +151,13 @@ def __init__(
proxies=self._account.proxies,
verify=self._account.verify,
private_endpoint=self._account.private_endpoint,
url_resolver=url_resolver,
)

self._channel_strategy = channel_strategy or self._account.channel_strategy
self._channel = self._account.channel
self._backend_allowed_list: List[str] = []
self._url_resolver = url_resolver

if self._channel == "ibm_cloud":
self._api_client = RuntimeClient(self._client_params)
Expand Down Expand Up @@ -359,6 +363,7 @@ def _initialize_hgps(
),
proxies=self._account.proxies,
verify=self._account.verify,
url_resolver=self._url_resolver,
)

# Build the hgp.
Expand Down
1 change: 1 addition & 0 deletions qiskit_ibm_runtime/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
to_python_identifier,
is_crn,
get_runtime_api_base_url,
default_runtime_url_resolver,
resolve_crn,
are_circuits_dynamic,
)
Expand Down
16 changes: 15 additions & 1 deletion qiskit_ibm_runtime/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from qiskit.circuit import QuantumCircuit, ControlFlowOp
from qiskit.transpiler import Target
from qiskit.providers.backend import BackendV1, BackendV2
from .deprecation import deprecate_function


def is_simulator(backend: BackendV1 | BackendV2) -> bool:
Expand Down Expand Up @@ -157,7 +158,20 @@ def is_crn(locator: str) -> bool:
return isinstance(locator, str) and locator.startswith("crn:")


def get_runtime_api_base_url(url: str, instance: str, private_endpoint: bool = False) -> str:
@deprecate_function(
"get_runtime_api_base_url()",
"0.30.0",
"Please use default_runtime_url_resolver() instead.",
stacklevel=1,
)
def get_runtime_api_base_url(
url: str, instance: str, private_endpoint: Optional[bool] = False
) -> str:
"""Computes the Runtime API base URL based on the provided input parameters."""
return default_runtime_url_resolver(url, instance, private_endpoint=private_endpoint)


def default_runtime_url_resolver(url: str, instance: str, private_endpoint: bool = False) -> str:
"""Computes the Runtime API base URL based on the provided input parameters.
Args:
Expand Down
1 change: 1 addition & 0 deletions release-notes/unreleased/1914.deprecation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate util function `get_runtime_api_base_url`: use `default_runtime_url_resolver` instead.
14 changes: 14 additions & 0 deletions release-notes/unreleased/1914.feat.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Add `url_resolver` optional input to :class:`.QiskitRuntimeService`
constructor to enable custom generation of the Qiskit Runtime API URL
based on the provided `url`, `instance` and `private_endpoint`. If
not specified, the default resolver will be used.

.. code:: python
# Define a custom resolver. In this case returns the concatenation of the provided `url` and the `instance`
def custom_url_resolver(url, instance, *args, **kwargs):
return f"{url}/{instance}"
service = QiskitRuntimeService(channel="ibm_quantum", instance="ibm-q/open/main", url="https://baseurl.org" url_resolver=custom_url_resolver)
# resulting resolved url will be: `https://baseurl.org/ibm-q/open/main`
Add util function `default_runtime_url_resolver`.
27 changes: 25 additions & 2 deletions test/unit/test_client_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,31 +57,52 @@ def test_get_runtime_api_base_url(self) -> None:
"ibm_cloud",
"crn:v1:bluemix:public:quantum-computing:us-east:a/...:...::",
"https://cloud.ibm.com",
None,
"https://us-east.quantum-computing.cloud.ibm.com",
),
(
"ibm_cloud",
"crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::",
"https://cloud.ibm.com",
None,
"https://my-region.quantum-computing.cloud.ibm.com",
),
(
"ibm_cloud",
"crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::",
"https://api-ntc-name.experimental-us-someid.us-east.containers.appdomain.cloud",
None,
"https://api-ntc-name.experimental-us-someid.us-east.containers.appdomain.cloud",
),
(
"ibm_quantum",
"h/g/p",
"https://auth.quantum-computing.ibm.com/api",
None,
"https://auth.quantum-computing.ibm.com/api",
),
(
"ibm_cloud",
"crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::",
"https://api-ntc-name.experimental-us-someid.us-east.containers.appdomain.cloud",
lambda a, b, c: f"{a}:{b}:{c}",
"https://api-ntc-name.experimental-us-someid.us-east.containers.appdomain.cloud:"
+ "crn:v1:bluemix:public:quantum-computing:my-region:a/...:...:::False",
),
(
"ibm_quantum",
"h/g/p",
"https://auth.quantum-computing.ibm.com/api",
lambda a, b, c: f"{a}:{b}:{c}",
"https://auth.quantum-computing.ibm.com/api:h/g/p:False",
),
]
for spec in test_specs:
channel, instance, url, expected = spec
channel, instance, url, url_resolver, expected = spec
with self.subTest(instance=instance, url=url):
params = self._get_client_params(channel=channel, instance=instance, url=url)
params = self._get_client_params(
channel=channel, instance=instance, url=url, url_resolver=url_resolver
)
self.assertEqual(params.get_runtime_api_base_url(), expected)

def test_proxies_param_with_ntlm(self) -> None:
Expand Down Expand Up @@ -153,6 +174,7 @@ def _get_client_params(
instance=None,
proxies=None,
verify=None,
url_resolver=None,
):
"""Return a custom ClientParameters."""
if verify is None:
Expand All @@ -164,4 +186,5 @@ def _get_client_params(
instance=instance,
proxies=proxies,
verify=verify,
url_resolver=url_resolver,
)