自定义API端点 (.api.services.)#
预计阅读时间:16分钟
你将学习的内容#
本指南的目标是向数据所有者展示如何扩展现有的Python API以实现:
公开无需审批即可运行的API方法(预批准代码)
创建API桥接以访问托管在第三方平台上的私有资产
简介#
自定义API端点是Python API的扩展,可在数据站点的Syft客户端中使用,允许管理员和数据所有者扩展研究人员可用的方法。
这并不是什么新鲜事:
事实上 - 这正是您在创建Syft函数时所做的事情。例如,如果您定义并提交了以下内容:
@syft_function_single_use(df=some_dataset)
def my_special_method():
...
您将扩展我们的 API,添加只能在获得批准后运行的自定义方法。
但在这种情况下,管理员会预先定义数据科学家可以运行的自定义方法,结合上述功能来实现各种目标。
让我们通过几个特别有用的场景来了解这一点。
1. 数据所有者: 我的私有数据存储在Microsoft Azure SQL数据库中,数据量太大无法迁移到PySyft
假设您在一个SQL数据库中存储了数十TB的数据。如果直接将其上传到PySyft的blob存储,您可能会考虑多个问题:
需要额外工作来托管最新的快照
复制数据成本高昂
SQL数据库在处理SQL查询方面非常高效,使用Microsoft Azure SQL引擎进行计算会更经济实惠
就像在PySyft上托管模拟和私有数据集资产一样,您可以定义一个通往模拟和私有数据集的桥梁。
2. 数据所有者: 我的模型托管在Google Vertex AI上,太大无法迁移到PySyft
假设您将专有模型存储在Google Vertex中。同样,您可能会担心如果直接在PySyft中托管它:
需要额外的工作来托管最新的模型检查点
模型的训练或推理需要大量资源(例如GPU),或者它针对TPU运行进行了优化;或者,简单来说,通过Google Vertex运行计算成本更低。
就像在PySyft上托管模拟和私有模型一样,您可以定义一个通往模拟和私有模型的桥梁。
2. 数据所有者: 我的模型托管在Google Vertex AI上,太大无法迁移到PySyft
假设您已经了解数据科学家可能需要和请求的内容 - 为了便于他们开展研究,您可以公开一些他们可以使用的公共API端点。
关于如何创建这样一个API桥接到第三方平台的详细操作指南可在此处获取。
Show code cell source
import syft as sy
datasite = sy.orchestra.launch(
name="test-datasite",
dev_mode=False,
create_producer=True,
n_consumers=1,
reset=True,
port="auto",
)
client = datasite.login(email="[email protected]", password="changethis")
client.register(
email="[email protected]",
password="verysecurepassword",
password_verify="verysecurepassword",
name="New User",
)
guest_client = datasite.login(email="[email protected]", password="verysecurepassword")
Show code cell output
Starting test-datasite server on 0.0.0.0:54319
Waiting for server to start Done.
You have launched a development server at http://0.0.0.0:54319.It is intended only for local use.
Logged into <test-datasite: High side Datasite> as <[email protected]>
You are using a default password. Please change the password using `[your_client].account.set_password([new_password])`.
Logged into <test-datasite: High side Datasite> as <[email protected]>
创建API端点#
公共API端点#
公共API端点本质上是一种无需您批准即可自由使用的方法。这是您公开预先批准方法的途径。
端点的元素包括:
一个带有以下参数的
sy.api_endpoint装饰器:path: 路径由API名称和子路径组成,格式为api_name.subpath;按此方式定义后,您的API将通过访问.api.services.api_name.subpath description: 支持使用markdown格式详细说明研究人员应如何使用它settings: 可选参数,这是一个字典,包含您可能想在API端点中访问的值。这种方式非常适合传递密钥API,因为这样没有人能看到或访问它们。
context: 始终是方法签名的一部分,这允许您访问设置变量或用户客户端,它看起来像这样:
@sy.api_endpoint(
path="my_api.subpath",
description="This is an example endpoint",
settings={"key": "value"}
)
def my_preapproved_func(
context,
query: str,
) -> Any:
pass
让我们来看一个例子:
@sy.api_endpoint(
path="my_org.sum_numbers",
description="This is an example endpoint",
)
def my_preapproved_func(
context,
number_1,
number_2
):
return number_1 + number_2
client.custom_api.add(endpoint=my_preapproved_func)
Endpoint successfully created.
让我们快速测试一下!
client.api.services.my_org.sum_numbers(number_1=2, number_2=3)
指针
5
注意
传递值 由于自定义API端点的定义方式,始终需要完整指定kwargs。
双生API端点#
正如我们之前所见,自定义API端点可以作为桥梁,通过模拟私有范式实现对私有资产的访问。
为此,您需要定义两种方法:一种用于模拟访问,另一种用于私有访问,然后创建最终端点,如下所示:
模拟方法:
@sy.api_endpoint_method(
settings={"key": MY_API_KEY}
)
def mock_method(context, query:str):
pass
私有方法:
@sy.api_endpoint_method(
settings={"key": MY_API_KEY}
)
def private_method(context, query:str):
pass
通过同时使用两者,您只需定义一个双端点,包括其路径和描述:
new_endpoint = sy.TwinAPIEndpoint(
path="my_org.my_data",
mock_function=mock_method,
private_function=private_method,
description="This is an example endpoint"
)
让我们看一个示例:
MOCK_STORAGE_API_KEY = ""
PRIVATE_STORAGE_API_KEY = ""
@sy.api_endpoint_method(
settings={"key": MOCK_STORAGE_API_KEY}
)
def mock_get_sample_method(context, length: int):
import pandas as pd
# CALL external API using the key
# result = call(key=context.settings.key, length=length)
return pd.DataFrame.from_dict({'data': ['mock_example', 'another_mock_example']})
@sy.api_endpoint_method(
settings={"key": PRIVATE_STORAGE_API_KEY}
)
def private_get_sample_method(context, length: int):
import pandas as pd
# CALL external API using the key
# result = call(key=context.settings.key, length=length)
return pd.DataFrame.from_dict({'data': ['private_example', 'another_private_example']})
new_endpoint = sy.TwinAPIEndpoint(
path="my_org.get_data_sample",
mock_function=mock_get_sample_method,
private_function=private_get_sample_method,
description="Get a fixed number of rows of the data."
)
client.custom_api.add(endpoint=new_endpoint)
Endpoint successfully created.
上下文管理器#
如上所示,方法签名中包含一个context参数。它的作用是帮助您:
设置: 访问设置和任何非公开的密钥,例如API密钥
示例:
context.settings["key]有状态API: 保持状态 - 用户可以跨同一API的多次运行保存状态,从而能够基于先前状态进行构建或实现诸如速率限制等功能
示例:
context.state["time_of_run"] = time()访问运行用户的相关元数据
示例
context.user.email访问运行用户的客户端: 访问运行用户的客户端以获取各种信息,例如电子邮件,甚至可以代表他们执行操作,例如提交查询。
示例
context.user_client.code.request_code_execution(execute_query).您自己的外部代码: 您可以通过`helper_functions`将流程中定义的外部方法或类传递给API端点定义
示例:
def count_user_runs_in_last_hour(context.state):
# ...
@sy.api_endpoint_method(
helper_functions=[count_user_runs_in_last_hour]
):
context.code.count_user_runs_in_last_hour
# ...
这为管理员提供了极大的灵活性。如需查看示例,请参考此指南。
使用第三方库#
在尝试搭建与外部平台的桥梁时,使用第三方Python包(例如这些平台提供的用于与其API交互的Python库)是非常常见的需求。
在这种情况下,您可以配置自定义API端点,使其始终运行在自定义的工作池(运行适当的镜像)上。可以按如下方式指定:
公共API端点
@sy.api_endpoint(
path="my_org.sum_numbers",
description="This is an example endpoint",
worker_pool="math-libs-pool"
)
def my_preapproved_func(context, number_1, number_2):
import my_third_party_math_lib
return number_1 + number_2
双API端点
new_endpoint = sy.TwinAPIEndpoint(
path="my_org.get_data_sample",
mock_function=mock_get_sample_method,
private_function=private_get_sample_method,
description="Get a fixed number of rows of the data."
worker_pool="azure-sql-pool"
)
查看和使用API端点#
至此,自定义API端点行为的定义工作应该已经完成,我们希望了解如何以管理员、数据所有者或数据科学家的身份来使用它。
请注意,在此情况下管理员和数据所有者拥有相同的权限。
获取所有可用的自定义端点#
所有用户都可以通过以下方式检查可用的端点:
client.custom_api
TwinAPI端点列表
总计: 0
我们甚至可以通过使用上面定义的路径进一步查看各个端点,如下所示:
client.api.services.my_org.get_data_sample
API: my_org.get_data_sample
描述: 获取固定行数的数据。
私有代码:
def private_get_sample_method(context, length: int):
import pandas as pd
# CALL external API using the key
# result = call(key=context.settings.key, length=length)
return pd.DataFrame.from_dict({'data': ['private_example', 'another_private_example']})
公开代码:
def mock_get_sample_method(context, length: int):
import pandas as pd
# CALL external API using the key
# result = call(key=context.settings.key, length=length)
return pd.DataFrame.from_dict({'data': ['mock_example', 'another_mock_example']})
查看代码定义#
如上所示,作为管理员,我们可以查看所有已定义的代码。
默认情况下,数据科学家只能看到模拟定义。不过,这可以更新为让数据科学家完全看不到端点的运作方式(请查看下方的更新部分)。
guest_client.api.services.my_org.get_data_sample
API: my_org.get_data_sample
描述:获取固定行数的数据。
私有代码:
N / A
公开代码:
def mock_get_sample_method(context, length: int):
import pandas as pd
# CALL external API using the key
# result = call(key=context.settings.key, length=length)
return pd.DataFrame.from_dict({'data': ['mock_example', 'another_mock_example']})
直接运行#
对于公共端点,您不需要任何审批 - 可以直接运行并获取最终答案,无需任何人批准您的查询,如下所示:
# As a data scientist / data owner / admin
guest_client.api.services.my_org.sum_numbers(number_1=2, number_2=3)
指针
5
这将创建一个立即执行并返回结果的作业。可以通过client.jobs查看为特定端点创建的作业:
guest_client.jobs
任务列表
总计: 0
然而,对于同时拥有模拟版本和私有版本的Twin Endpoints来说,这是一个包含审批步骤的多阶段流程。
首先让我们看看在没有批准的情况下可以运行什么:
# As a data scientist / data owner / admin
guest_client.api.services.my_org.get_data_sample.mock(length=2)
| 数据 | |
|---|---|
| 0 | mock_example |
| 1 | another_mock_example |
# As a data scientist
guest_client.api.services.my_org.get_data_sample.private(length=2)
SyftError: You're not allowed to run this code.
SyftError: You're not allowed to run this code.
SyftError: You're not allowed to run this code.
You're not allowed to run this code.
# As a data owner / admin
client.api.services.my_org.get_data_sample.private(length=2)
| 数据 | |
|---|---|
| 0 | private_example |
| 1 | another_private_example |
警告
谁能访问私有数据 我们可以看到,数据科学家仅限于使用公共和模拟的自定义API运行。要运行真实的API,需要数据所有者的批准,这可以通过定义一个syft函数来实现。
在您的syft函数中使用它#
自定义API端点可以像服务器上的其他私有资产一样使用,并且可以作为输入策略的一部分进行指定。
注意
使用.mock或.private非常适合测试,类似于与数据集或模型交互的方式。然而,当将其作为输入端点传递时,我们期望您希望针对私有数据进行计算,因此PySyft只允许在获得批准后运行它。
@sy.syft_function_single_use(
data_endpoint=guest_client.api.services.my_org.get_data_sample)
def get_sample_fixed_length(data_endpoint):
data = data_endpoint(length=2)
return data.iloc[0]
guest_client.code.request_code_execution(get_sample_fixed_length)
Syft function 'get_sample_fixed_length' successfully created. To add a code request, please create a project using `project = syft.Project(...)`, then use command `project.create_code_request`.
请求
ID: 4dda4d363f9043a589f6f5eae9ff889c
请求时间:2024-08-24 19:48:04
状态:RequestStatus.PENDING
请求时间: 测试数据站点,类型为数据站点
请求者: 新用户 ([email protected])
变更内容: 请求将 get_sample_fixed_length (资源池ID: default-pool) 的权限状态更改为 RequestStatus.APPROVED。无嵌套请求。
guest_client.code.get_sample_fixed_length()
Your code is waiting for approval. Code status on server 'test-datasite' is 'UserCodeStatus.PENDING'.
让我们批准并重试:
# as data owner / admin
client.requests[-1].approve()
# as data scientist
result = guest_client.code.get_sample_fixed_length(data_endpoint=guest_client.api.services.my_org.get_data_sample)
result.get()
Approving request on change get_sample_fixed_length for datasite test-datasite
data private_example
Name: 0, dtype: object
更新API端点#
更新代码定义#
要更新代码定义,您可以定义一个新的模拟或私有方法并按如下方式更新:
@sy.api_endpoint_method(
settings={"key": MOCK_STORAGE_API_KEY}
)
def new_mock_get_sample_method(context, length: int):
import pandas as pd
return "Oh, look, I updated the mock function!"
client.custom_api.update(endpoint_path="my_org.get_data_sample", mock_function=new_mock_get_sample_method)
Endpoint successfully updated.
client.api.services.my_org.get_data_sample.mock(length=2)
指针
‘哦,看,我更新了模拟函数!’
你可以对private_function进行类似操作。
更新读取代码的权限#
如前所述,默认情况下数据科学家可以访问模拟定义。在某些情况下,这非常有用,能让数据科学家理解模拟响应背后的假设,从而查看代码。
不过,可以通过更新端点的相应设置来隐藏它:
client.custom_api.update(endpoint_path="my_org.get_data_sample", hide_mock_definition=True)
Endpoint successfully updated.
# as data scientist, now:
guest_client.api.services.my_org.get_data_sample
API: my_org.get_data_sample
描述: 获取固定行数的数据。
私有代码:
N / A
公开代码:
N / A
更新超时时长#
这指定了API端点超时的秒数。默认情况下,该值为60秒。
client.custom_api.update(endpoint_path="my_org.get_data_sample", endpoint_timeout=120) # Update to 120s
Endpoint successfully updated.
删除API端点#
删除一个API端点非常简单:
client.custom_api.delete(endpoint_path="my_org.get_data_sample")
Endpoint successfully deleted.
client.custom_api
TwinAPI端点列表