Skip to content

Commit 940a19c

Browse files
author
灵轮
committed
feat: add opt-in VPC endpoints for knowledge base retrieval
Allow Bailian, ADB, and OTS knowledge base calls to use VPC endpoints via AGENTRUN_KB_USE_VPC while keeping public network as the default behavior. Change-Id: I8b51c2cf7f9089bc4e0a9b1609c119c126b7273a Co-developed-by: Cursor <noreply@cursor.com>
1 parent f35f01f commit 940a19c

8 files changed

Lines changed: 164 additions & 19 deletions

File tree

agentrun/knowledgebase/api/__data_async_template.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -661,8 +661,9 @@ def _build_agent_storage_client(
661661
raise ValueError("provider_settings is required for OTS retrieval")
662662

663663
cfg = Config.with_configs(self.config, config)
664-
region_id = cfg.get_region_id()
665-
ots_endpoint = f"http://ots-{region_id}.aliyuncs.com"
664+
ots_endpoint = cfg.get_ots_endpoint(
665+
self.provider_settings.ots_instance_name
666+
)
666667

667668
return AgentStorageClient(
668669
access_key_id=cfg.get_access_key_id(),

agentrun/knowledgebase/api/data.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -941,8 +941,9 @@ def _build_agent_storage_client(
941941
raise ValueError("provider_settings is required for OTS retrieval")
942942

943943
cfg = Config.with_configs(self.config, config)
944-
region_id = cfg.get_region_id()
945-
ots_endpoint = f"http://ots-{region_id}.aliyuncs.com"
944+
ots_endpoint = cfg.get_ots_endpoint(
945+
self.provider_settings.ots_instance_name
946+
)
946947

947948
return AgentStorageClient(
948949
access_key_id=cfg.get_access_key_id(),

agentrun/utils/config.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
load_dotenv()
1313

1414

15+
_GPDB_PUBLIC_SHARED_REGIONS = (
16+
"cn-beijing",
17+
"cn-hangzhou",
18+
"cn-shanghai",
19+
"cn-shenzhen",
20+
"cn-hongkong",
21+
"ap-southeast-1",
22+
)
23+
24+
1525
def get_env_with_default(default: str, *key: str) -> str:
1626
"""从环境变量获取值,支持多个候选键 / Get value from environment variables with multiple fallback keys
1727
@@ -29,6 +39,15 @@ def get_env_with_default(default: str, *key: str) -> str:
2939
return default
3040

3141

42+
def get_env_flag(*key: str) -> bool:
43+
"""从环境变量读取布尔开关 / Read boolean flag from environment variables"""
44+
for k in key:
45+
v = os.getenv(k)
46+
if v is not None:
47+
return v.strip().lower() in ("1", "true", "yes", "on")
48+
return False
49+
50+
3251
class Config:
3352
"""AgentRun SDK 全局配置类 / AgentRun SDK Global Configuration Class
3453
@@ -62,6 +81,7 @@ class Config:
6281
"_data_endpoint",
6382
"_devs_endpoint",
6483
"_bailian_endpoint",
84+
"_use_vpc_endpoint",
6585
"_headers",
6686
"__weakref__",
6787
)
@@ -80,6 +100,7 @@ def __init__(
80100
data_endpoint: Optional[str] = None,
81101
devs_endpoint: Optional[str] = None,
82102
bailian_endpoint: Optional[str] = None,
103+
use_vpc_endpoint: Optional[bool] = None,
83104
headers: Optional[Dict[str, str]] = None,
84105
) -> None:
85106
"""初始化配置 / Initialize configuration
@@ -103,7 +124,9 @@ def __init__(
103124
read_timeout: 读取超时时间(秒),默认 100000 / Read timeout in seconds, defaults to 100000
104125
control_endpoint: 自定义控制链路端点,可选 / Custom control endpoint, optional
105126
data_endpoint: 自定义数据链路端点,可选 / Custom data endpoint, optional
106-
devs_endpoint: 自定义 DevS 端点,可选 / Custom DevS endpoint, optional
127+
devs_endpoint: 自定义 Devs 端点,可选 / Custom DevS endpoint, optional
128+
use_vpc_endpoint: 知识库检索是否使用 VPC 内网 endpoint,默认 false
129+
未提供时从环境变量读取: AGENTRUN_KB_USE_VPC
107130
headers: 自定义请求头,可选 / Custom request headers, optional
108131
"""
109132

@@ -139,6 +162,8 @@ def __init__(
139162
devs_endpoint = get_env_with_default("", "DEVS_ENDPOINT")
140163
if bailian_endpoint is None:
141164
bailian_endpoint = get_env_with_default("", "BAILIAN_ENDPOINT")
165+
if use_vpc_endpoint is None:
166+
use_vpc_endpoint = get_env_flag("AGENTRUN_KB_USE_VPC")
142167

143168
self._access_key_id = access_key_id
144169
self._access_key_secret = access_key_secret
@@ -152,6 +177,7 @@ def __init__(
152177
self._data_endpoint = data_endpoint
153178
self._devs_endpoint = devs_endpoint
154179
self._bailian_endpoint = bailian_endpoint
180+
self._use_vpc_endpoint = use_vpc_endpoint
155181
self._headers = headers or {}
156182

157183
@classmethod
@@ -263,8 +289,33 @@ def get_bailian_endpoint(self) -> str:
263289
if self._bailian_endpoint:
264290
return self._bailian_endpoint
265291

292+
if self._use_vpc_endpoint:
293+
return f"bailian-vpc.{self.get_region_id()}.aliyuncs.com"
294+
266295
return "https://bailian.cn-beijing.aliyuncs.com"
267296

297+
def get_gpdb_endpoint(self) -> str:
298+
"""获取 GPDB (ADB) OpenAPI 端点 / Get GPDB (ADB) OpenAPI endpoint"""
299+
region_id = self.get_region_id()
300+
if self._use_vpc_endpoint:
301+
return f"gpdb-vpc.{region_id}.aliyuncs.com"
302+
if region_id in _GPDB_PUBLIC_SHARED_REGIONS:
303+
return "gpdb.aliyuncs.com"
304+
return f"gpdb.{region_id}.aliyuncs.com"
305+
306+
def get_ots_endpoint(self, instance_name: str) -> str:
307+
"""获取 OTS endpoint / Get OTS endpoint"""
308+
region_id = self.get_region_id()
309+
if self._use_vpc_endpoint:
310+
return (
311+
f"https://{instance_name}.{region_id}.vpc.tablestore.aliyuncs.com"
312+
)
313+
return f"http://ots-{region_id}.aliyuncs.com"
314+
315+
def get_use_vpc_endpoint(self) -> bool:
316+
"""知识库检索是否使用 VPC 内网 endpoint"""
317+
return self._use_vpc_endpoint
318+
268319
def get_headers(self) -> Dict[str, str]:
269320
"""获取自定义请求头"""
270321
return self._headers or {}

agentrun/utils/control_api.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,7 @@ def _get_gpdb_client(self, config: Optional[Config] = None) -> "GPDBClient":
126126
from alibabacloud_gpdb20160503.client import Client as GPDBClient
127127

128128
cfg = Config.with_configs(self.config, config)
129-
# GPDB 使用区域级别的 endpoint / GPDB uses region-level endpoint
130-
region_id = cfg.get_region_id()
131-
if region_id in (
132-
"cn-beijing",
133-
"cn-hangzhou",
134-
"cn-shanghai",
135-
"cn-shenzhen",
136-
"cn-hongkong",
137-
"ap-southeast-1",
138-
):
139-
endpoint = "gpdb.aliyuncs.com"
140-
else:
141-
endpoint = f"gpdb.{region_id}.aliyuncs.com"
129+
endpoint = cfg.get_gpdb_endpoint()
142130

143131
return GPDBClient(
144132
open_api_util_models.Config(

examples/knowledgebase.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- AGENTRUN_ACCESS_KEY_ID: 阿里云 AccessKey ID
1717
- AGENTRUN_ACCESS_KEY_SECRET: 阿里云 AccessKey Secret
1818
- AGENTRUN_REGION: 区域(默认 cn-hangzhou)
19+
- AGENTRUN_KB_USE_VPC: 设为 true 时,百炼/ADB/OTS 知识库检索走 VPC 内网 endpoint(默认 false,RagFlow 不受影响)
1920
2021
百炼知识库额外配置 / Additional config for Bailian:
2122
- BAILIAN_WORKSPACE_ID: 百炼工作空间 ID

tests/unittests/knowledgebase/test_ots_knowledgebase.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,9 @@ class TestOTSDataAPIBuildClient:
597597
def test_build_client(self, mock_client_class):
598598
"""测试构建客户端"""
599599
mock_config = MagicMock(spec=Config)
600-
mock_config.get_region_id.return_value = "cn-hangzhou"
600+
mock_config.get_ots_endpoint.return_value = (
601+
"http://ots-cn-hangzhou.aliyuncs.com"
602+
)
601603
mock_config.get_access_key_id.return_value = "test-ak"
602604
mock_config.get_access_key_secret.return_value = "test-sk"
603605
mock_config.get_security_token.return_value = "test-sts"
@@ -619,6 +621,36 @@ def test_build_client(self, mock_client_class):
619621
ots_instance_name="test-instance",
620622
)
621623

624+
@patch("agentrun.knowledgebase.api.data.AgentStorageClient")
625+
def test_build_client_vpc_mode(self, mock_client_class):
626+
"""测试 VPC 模式构建 OTS 客户端"""
627+
mock_config = MagicMock(spec=Config)
628+
mock_config.get_ots_endpoint.return_value = (
629+
"https://test-instance.cn-hangzhou.vpc.tablestore.aliyuncs.com"
630+
)
631+
mock_config.get_access_key_id.return_value = "test-ak"
632+
mock_config.get_access_key_secret.return_value = "test-sk"
633+
mock_config.get_security_token.return_value = "test-sts"
634+
635+
with patch.object(Config, "with_configs", return_value=mock_config):
636+
api = OTSDataAPI(
637+
"test-kb",
638+
provider_settings=OTSProviderSettings(
639+
ots_instance_name="test-instance"
640+
),
641+
)
642+
api._build_agent_storage_client()
643+
644+
mock_client_class.assert_called_once_with(
645+
access_key_id="test-ak",
646+
access_key_secret="test-sk",
647+
sts_token="test-sts",
648+
ots_endpoint=(
649+
"https://test-instance.cn-hangzhou.vpc.tablestore.aliyuncs.com"
650+
),
651+
ots_instance_name="test-instance",
652+
)
653+
622654
def test_build_client_without_provider_settings(self):
623655
"""测试无 provider_settings 时构建客户端"""
624656
api = OTSDataAPI("test-kb")

tests/unittests/utils/test_config.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,34 @@ def test_init_without_parameters(self):
1919
assert config._access_key_id == "mock-access-key-id"
2020
assert config._access_key_secret == "mock-access-key-secret"
2121
assert config._account_id == "mock-account-id"
22+
assert config._use_vpc_endpoint is False
23+
24+
def test_use_vpc_endpoint_from_env(self):
25+
with patch.dict(os.environ, {"AGENTRUN_KB_USE_VPC": "true"}, clear=False):
26+
config = Config()
27+
assert config.get_use_vpc_endpoint() is True
28+
29+
def test_kb_endpoints_default_public(self):
30+
config = Config(region_id="cn-hangzhou")
31+
assert config.get_bailian_endpoint() == "https://bailian.cn-beijing.aliyuncs.com"
32+
assert config.get_gpdb_endpoint() == "gpdb.aliyuncs.com"
33+
assert config.get_ots_endpoint("my-instance") == (
34+
"http://ots-cn-hangzhou.aliyuncs.com"
35+
)
36+
37+
def test_kb_endpoints_vpc_mode(self):
38+
config = Config(region_id="cn-hangzhou", use_vpc_endpoint=True)
39+
assert config.get_bailian_endpoint() == (
40+
"bailian-vpc.cn-hangzhou.aliyuncs.com"
41+
)
42+
assert config.get_gpdb_endpoint() == "gpdb-vpc.cn-hangzhou.aliyuncs.com"
43+
assert config.get_ots_endpoint("my-instance") == (
44+
"https://my-instance.cn-hangzhou.vpc.tablestore.aliyuncs.com"
45+
)
46+
47+
def test_bailian_endpoint_override_takes_precedence(self):
48+
config = Config(
49+
use_vpc_endpoint=True,
50+
bailian_endpoint="custom.bailian.example.com",
51+
)
52+
assert config.get_bailian_endpoint() == "custom.bailian.example.com"

tests/unittests/utils/test_control_api.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,26 @@ def test_get_bailian_client_strips_http_prefix(self, mock_client_class):
342342
config_arg = call_args[0][0]
343343
assert config_arg.endpoint == "bailian.custom.com"
344344

345+
@patch("alibabacloud_bailian20231229.client.Client")
346+
def test_get_bailian_client_vpc_mode(self, mock_client_class):
347+
"""测试 VPC 模式使用 bailian-vpc endpoint"""
348+
config = Config(
349+
access_key_id="ak",
350+
access_key_secret="sk",
351+
region_id="cn-beijing",
352+
use_vpc_endpoint=True,
353+
)
354+
api = ControlAPI(config=config)
355+
356+
mock_client = MagicMock()
357+
mock_client_class.return_value = mock_client
358+
359+
api._get_bailian_client()
360+
361+
call_args = mock_client_class.call_args
362+
config_arg = call_args[0][0]
363+
assert config_arg.endpoint == "bailian-vpc.cn-beijing.aliyuncs.com"
364+
345365

346366
class TestControlAPIGetGPDBClient:
347367
"""测试 ControlAPI._get_gpdb_client"""
@@ -413,3 +433,23 @@ def test_get_gpdb_client_all_known_regions(self, mock_client_class):
413433
assert (
414434
config_arg.endpoint == "gpdb.aliyuncs.com"
415435
), f"Region {region} should use gpdb.aliyuncs.com"
436+
437+
@patch("alibabacloud_gpdb20160503.client.Client")
438+
def test_get_gpdb_client_vpc_mode(self, mock_client_class):
439+
"""测试 VPC 模式使用 gpdb-vpc endpoint"""
440+
config = Config(
441+
access_key_id="ak",
442+
access_key_secret="sk",
443+
region_id="cn-hangzhou",
444+
use_vpc_endpoint=True,
445+
)
446+
api = ControlAPI(config=config)
447+
448+
mock_client = MagicMock()
449+
mock_client_class.return_value = mock_client
450+
451+
api._get_gpdb_client()
452+
453+
call_args = mock_client_class.call_args
454+
config_arg = call_args[0][0]
455+
assert config_arg.endpoint == "gpdb-vpc.cn-hangzhou.aliyuncs.com"

0 commit comments

Comments
 (0)