Skip to content

feat:add apollo configuration to load env file #11210

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

Merged
merged 16 commits into from
Dec 9, 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
73 changes: 66 additions & 7 deletions api/configs/app_config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
from pydantic_settings import SettingsConfigDict
import logging
from typing import Any

from configs.deploy import DeploymentConfig
from configs.enterprise import EnterpriseFeatureConfig
from configs.extra import ExtraServiceConfig
from configs.feature import FeatureConfig
from configs.middleware import MiddlewareConfig
from configs.packaging import PackagingInfo
from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict

from .deploy import DeploymentConfig
from .enterprise import EnterpriseFeatureConfig
from .extra import ExtraServiceConfig
from .feature import FeatureConfig
from .middleware import MiddlewareConfig
from .packaging import PackagingInfo
from .remote_settings_sources import RemoteSettingsSource, RemoteSettingsSourceConfig, RemoteSettingsSourceName
from .remote_settings_sources.apollo import ApolloSettingsSource

logger = logging.getLogger(__name__)


class RemoteSettingsSourceFactory(PydanticBaseSettingsSource):
def __init__(self, settings_cls: type[BaseSettings]):
super().__init__(settings_cls)

def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
raise NotImplementedError

def __call__(self) -> dict[str, Any]:
current_state = self.current_state
remote_source_name = current_state.get("REMOTE_SETTINGS_SOURCE_NAME")
if not remote_source_name:
return {}

remote_source: RemoteSettingsSource | None = None
match remote_source_name:
case RemoteSettingsSourceName.APOLLO:
remote_source = ApolloSettingsSource(current_state)
case _:
logger.warning(f"Unsupported remote source: {remote_source_name}")
return {}

d: dict[str, Any] = {}

for field_name, field in self.settings_cls.model_fields.items():
field_value, field_key, value_is_complex = remote_source.get_field_value(field, field_name)
field_value = remote_source.prepare_field_value(field_name, field, field_value, value_is_complex)
if field_value is not None:
d[field_key] = field_value

return d


class DifyConfig(
Expand All @@ -19,6 +59,8 @@ class DifyConfig(
MiddlewareConfig,
# Extra service configs
ExtraServiceConfig,
# Remote source configs
RemoteSettingsSourceConfig,
# Enterprise feature configs
# **Before using, please contact [email protected] by email to inquire about licensing matters.**
EnterpriseFeatureConfig,
Expand All @@ -35,3 +77,20 @@ class DifyConfig(
# please consider to arrange it in the proper config group of existed or added
# for better readability and maintainability.
# Thanks for your concentration and consideration.

@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
env_settings,
RemoteSettingsSourceFactory(settings_cls),
dotenv_settings,
file_secret_settings,
)
2 changes: 1 addition & 1 deletion api/configs/middleware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class KeywordStoreConfig(BaseSettings):
)


class DatabaseConfig:
class DatabaseConfig(BaseSettings):
DB_HOST: str = Field(
description="Hostname or IP address of the database server.",
default="localhost",
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/storage/baidu_obs_storage_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field
from pydantic import Field
from pydantic_settings import BaseSettings


class BaiduOBSStorageConfig(BaseModel):
class BaiduOBSStorageConfig(BaseSettings):
"""
Configuration settings for Baidu Object Storage Service (OBS)
"""
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/storage/huawei_obs_storage_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field
from pydantic import Field
from pydantic_settings import BaseSettings


class HuaweiCloudOBSStorageConfig(BaseModel):
class HuaweiCloudOBSStorageConfig(BaseSettings):
"""
Configuration settings for Huawei Cloud Object Storage Service (OBS)
"""
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/storage/supabase_storage_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field
from pydantic import Field
from pydantic_settings import BaseSettings


class SupabaseStorageConfig(BaseModel):
class SupabaseStorageConfig(BaseSettings):
"""
Configuration settings for Supabase Object Storage Service
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field
from pydantic import Field
from pydantic_settings import BaseSettings


class VolcengineTOSStorageConfig(BaseModel):
class VolcengineTOSStorageConfig(BaseSettings):
"""
Configuration settings for Volcengine Tinder Object Storage (TOS)
"""
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/vdb/analyticdb_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field, PositiveInt
from pydantic import Field, PositiveInt
from pydantic_settings import BaseSettings


class AnalyticdbConfig(BaseModel):
class AnalyticdbConfig(BaseSettings):
"""
Configuration for connecting to Alibaba Cloud AnalyticDB for PostgreSQL.
Refer to the following documentation for details on obtaining credentials:
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/vdb/couchbase_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field
from pydantic import Field
from pydantic_settings import BaseSettings


class CouchbaseConfig(BaseModel):
class CouchbaseConfig(BaseSettings):
"""
Couchbase configs
"""
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/vdb/myscale_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from pydantic import BaseModel, Field, PositiveInt
from pydantic import Field, PositiveInt
from pydantic_settings import BaseSettings


class MyScaleConfig(BaseModel):
class MyScaleConfig(BaseSettings):
"""
Configuration settings for MyScale vector database
"""
Expand Down
5 changes: 3 additions & 2 deletions api/configs/middleware/vdb/vikingdb_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from pydantic import BaseModel, Field
from pydantic import Field
from pydantic_settings import BaseSettings


class VikingDBConfig(BaseModel):
class VikingDBConfig(BaseSettings):
"""
Configuration for connecting to Volcengine VikingDB.
Refer to the following documentation for details on obtaining credentials:
Expand Down
17 changes: 17 additions & 0 deletions api/configs/remote_settings_sources/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Optional

from pydantic import Field

from .apollo import ApolloSettingsSourceInfo
from .base import RemoteSettingsSource
from .enums import RemoteSettingsSourceName


class RemoteSettingsSourceConfig(ApolloSettingsSourceInfo):
REMOTE_SETTINGS_SOURCE_NAME: Optional[RemoteSettingsSourceName] = Field(
description="name of remote config source",
default=None,
)


__all__ = ["RemoteSettingsSource", "RemoteSettingsSourceConfig", "RemoteSettingsSourceName"]
55 changes: 55 additions & 0 deletions api/configs/remote_settings_sources/apollo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from collections.abc import Mapping
from typing import Any, Optional

from pydantic import Field
from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings

from configs.remote_settings_sources.base import RemoteSettingsSource

from .client import ApolloClient


class ApolloSettingsSourceInfo(BaseSettings):
"""
Packaging build information
"""

APOLLO_APP_ID: Optional[str] = Field(
description="apollo app_id",
default=None,
)

APOLLO_CLUSTER: Optional[str] = Field(
description="apollo cluster",
default=None,
)

APOLLO_CONFIG_URL: Optional[str] = Field(
description="apollo config url",
default=None,
)

APOLLO_NAMESPACE: Optional[str] = Field(
description="apollo namespace",
default=None,
)


class ApolloSettingsSource(RemoteSettingsSource):
def __init__(self, configs: Mapping[str, Any]):
self.client = ApolloClient(
app_id=configs["APOLLO_APP_ID"],
cluster=configs["APOLLO_CLUSTER"],
config_url=configs["APOLLO_CONFIG_URL"],
start_hot_update=False,
_notification_map={configs["APOLLO_NAMESPACE"]: -1},
)
self.namespace = configs["APOLLO_NAMESPACE"]
self.remote_configs = self.client.get_all_dicts(self.namespace)

def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
if not isinstance(self.remote_configs, dict):
raise ValueError(f"remote configs is not dict, but {type(self.remote_configs)}")
field_value = self.remote_configs.get(field_name)
return field_value, field_name, False
Loading
Loading