Showing
22 changed files
with
655 additions
and
167 deletions
| 1 | -# Byte-compiled / optimized / DLL files | ||
| 2 | -__pycache__/ | ||
| 3 | -*.py[cod] | ||
| 4 | -*$py.class | ||
| 5 | - | ||
| 6 | -# C extensions | ||
| 7 | -*.so | ||
| 8 | - | ||
| 9 | -# Distribution / packaging | ||
| 10 | -.Python | ||
| 11 | -build/ | ||
| 12 | -develop-eggs/ | ||
| 13 | -dist/ | ||
| 14 | -downloads/ | ||
| 15 | -eggs/ | ||
| 16 | -.eggs/ | ||
| 17 | -lib/ | ||
| 18 | -lib64/ | ||
| 19 | -parts/ | ||
| 20 | -sdist/ | ||
| 21 | -var/ | ||
| 22 | -wheels/ | ||
| 23 | -share/python-wheels/ | ||
| 24 | -*.egg-info/ | ||
| 25 | -.installed.cfg | ||
| 26 | -*.egg | ||
| 27 | -MANIFEST | ||
| 28 | - | ||
| 29 | -# PyInstaller | ||
| 30 | -# Usually these files are written by a python script from a template | ||
| 31 | -# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
| 32 | -*.manifest | ||
| 33 | -*.spec | ||
| 34 | - | ||
| 35 | -# Installer logs | ||
| 36 | -pip-log.txt | ||
| 37 | -pip-delete-this-directory.txt | ||
| 38 | - | ||
| 39 | -# Unit test / coverage reports | ||
| 40 | -htmlcov/ | ||
| 41 | -.tox/ | ||
| 42 | -.nox/ | ||
| 43 | -.coverage | ||
| 44 | -.coverage.* | ||
| 45 | -.cache | ||
| 46 | -nosetests.xml | ||
| 47 | -coverage.xml | ||
| 48 | -*.cover | ||
| 49 | -*.py,cover | ||
| 50 | -.hypothesis/ | ||
| 51 | -.pytest_cache/ | ||
| 52 | -cover/ | ||
| 53 | - | ||
| 54 | -# Translations | ||
| 55 | -*.mo | ||
| 56 | -*.pot | ||
| 57 | - | ||
| 58 | -# Django stuff: | ||
| 59 | -*.log | ||
| 60 | -local_settings.py | ||
| 61 | -db.sqlite3 | ||
| 62 | -db.sqlite3-journal | ||
| 63 | - | ||
| 64 | -# Flask stuff: | ||
| 65 | -instance/ | ||
| 66 | -.webassets-cache | ||
| 67 | - | ||
| 68 | -# Scrapy stuff: | ||
| 69 | -.scrapy | ||
| 70 | - | ||
| 71 | -# Sphinx documentation | ||
| 72 | -docs/_build/ | ||
| 73 | - | ||
| 74 | -# PyBuilder | ||
| 75 | -.pybuilder/ | ||
| 76 | -target/ | ||
| 77 | - | ||
| 78 | -# Jupyter Notebook | ||
| 79 | -.ipynb_checkpoints | ||
| 80 | - | ||
| 81 | -# IPython | ||
| 82 | -profile_default/ | ||
| 83 | -ipython_config.py | ||
| 84 | - | ||
| 85 | -# pyenv | ||
| 86 | -# For a library or package, you might want to ignore these files since the code is | ||
| 87 | -# intended to run in multiple environments; otherwise, check them in: | ||
| 88 | -# .python-version | ||
| 89 | - | ||
| 90 | -# pipenv | ||
| 91 | -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
| 92 | -# However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
| 93 | -# having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
| 94 | -# install all needed dependencies. | ||
| 95 | -#Pipfile.lock | ||
| 96 | - | ||
| 97 | -# poetry | ||
| 98 | -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. | ||
| 99 | -# This is especially recommended for binary packages to ensure reproducibility, and is more | ||
| 100 | -# commonly ignored for libraries. | ||
| 101 | -#poetry.lock | ||
| 102 | - | ||
| 103 | -# pdm | ||
| 104 | -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. | ||
| 105 | -#pdm.lock | ||
| 106 | -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it | ||
| 107 | -# in version control. Use .pdm.toml for personal settings and store shared settings in pyproject.toml. | ||
| 108 | -.pdm.toml | ||
| 109 | - | ||
| 110 | -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm | ||
| 111 | -__pypackages__/ | ||
| 112 | - | ||
| 113 | -# Celery stuff | ||
| 114 | -celerybeat-schedule | ||
| 115 | -celerybeat.pid | ||
| 116 | - | ||
| 117 | -# SageMath parsed files | ||
| 118 | -*.sage.py | ||
| 119 | - | ||
| 120 | -# Environments | ||
| 121 | -.env | ||
| 122 | -.venv | ||
| 123 | -env/ | ||
| 124 | -venv/ | ||
| 125 | -ENV/ | ||
| 126 | -environment.yml | ||
| 127 | - | ||
| 128 | -# Spyder project settings | ||
| 129 | -.spyderproject | ||
| 130 | -.spyproject | ||
| 131 | - | ||
| 132 | -# Rope project settings | ||
| 133 | -.ropeproject | ||
| 134 | - | ||
| 135 | -# mkdocs documentation | ||
| 136 | -/site | ||
| 137 | - | ||
| 138 | -# mypy | ||
| 139 | -.mypy_cache/ | ||
| 140 | -.dmypy.json | ||
| 141 | -dmypy.json | ||
| 142 | - | ||
| 143 | -# Pyre type checker | ||
| 144 | -.pyre/ | ||
| 145 | - | ||
| 146 | -# pytype static type analyzer | ||
| 147 | -.pytype/ | ||
| 148 | - | ||
| 149 | -# Cython debug symbols | ||
| 150 | -cython_debug/ | ||
| 151 | - | ||
| 152 | -# PyCharm | ||
| 153 | -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can | ||
| 154 | -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | ||
| 155 | -# and can be added to the global gitignore or merged into this file. | ||
| 156 | -# For a more comprehensive ignore file, see: | ||
| 157 | -# https://github.com/github/gitignore/blob/main/Python.gitignore | ||
| 158 | - | ||
| 159 | -# VS Code | 1 | +/live-stream-ai-task-workline/modules/wtt_highlight/model_weights/* |
| 2 | +*mov | ||
| 3 | +/.externalToolBuilders | ||
| 4 | +/.settings | ||
| 5 | +.svn | ||
| 6 | +/.project | ||
| 7 | +/target | ||
| 8 | +./target | ||
| 9 | +/.classpath | ||
| 10 | +.idea/ | ||
| 11 | +*.iml | ||
| 12 | +*.iws | ||
| 13 | +*.ipr | ||
| 14 | +*.ids | ||
| 15 | +*.orig | ||
| 16 | +classes/ | ||
| 17 | +/bin/ | ||
| 18 | +*/.classpath | ||
| 19 | +*/.project | ||
| 20 | +*/.settings/* | ||
| 21 | +###################### | ||
| 22 | +# Project Specific | ||
| 23 | +###################### | ||
| 24 | +/target/www/** | ||
| 25 | +/src/test/javascript/coverage/ | ||
| 26 | +/src/test/javascript/PhantomJS*/ | ||
| 27 | + | ||
| 28 | +###################### | ||
| 29 | +# Node | ||
| 30 | +###################### | ||
| 31 | +/node/ | ||
| 32 | +node_tmp/ | ||
| 33 | +node_modules/ | ||
| 34 | +npm-debug.log.* | ||
| 35 | +/.awcache/* | ||
| 36 | + | ||
| 37 | +###################### | ||
| 38 | +# SASS | ||
| 39 | +###################### | ||
| 40 | +.sass-cache/ | ||
| 41 | + | ||
| 42 | +###################### | ||
| 43 | +# Eclipse | ||
| 44 | +###################### | ||
| 45 | +*.pydevproject | ||
| 46 | +.project | ||
| 47 | +.metadata | ||
| 48 | +tmp/ | ||
| 49 | +tmp/**/* | ||
| 50 | +*.tmp | ||
| 51 | +*.bak | ||
| 52 | +*.swp | ||
| 53 | +*~.nib | ||
| 54 | +local.properties | ||
| 55 | +.classpath | ||
| 56 | +.settings/ | ||
| 57 | +.loadpath | ||
| 58 | +.factorypath | ||
| 59 | +/src/main/resources/rebel.xml | ||
| 60 | + | ||
| 61 | +# External tool builders | ||
| 62 | +.externalToolBuilders/** | ||
| 63 | + | ||
| 64 | +# Locally stored "Eclipse launch configurations" | ||
| 65 | +*.launch | ||
| 66 | + | ||
| 67 | +# CDT-specific | ||
| 68 | +.cproject | ||
| 69 | + | ||
| 70 | +# PDT-specific | ||
| 71 | +.buildpath | ||
| 72 | + | ||
| 73 | +###################### | ||
| 74 | +# Intellij | ||
| 75 | +###################### | ||
| 76 | +.idea/ | ||
| 77 | +*.iml | ||
| 78 | +*.iws | ||
| 79 | +*.ipr | ||
| 80 | +*.ids | ||
| 81 | +*.orig | ||
| 82 | +classes/ | ||
| 83 | + | ||
| 84 | +###################### | ||
| 85 | +# Visual Studio Code | ||
| 86 | +###################### | ||
| 160 | .vscode/ | 87 | .vscode/ |
| 161 | 88 | ||
| 89 | +###################### | ||
| 90 | +# Maven | ||
| 91 | +###################### | ||
| 92 | +/log/ | ||
| 93 | +/target/ | ||
| 94 | + | ||
| 95 | +###################### | ||
| 96 | +# Gradle | ||
| 97 | +###################### | ||
| 98 | +.gradle/ | ||
| 99 | +/build/ | ||
| 100 | + | ||
| 101 | +###################### | ||
| 102 | +# Package Files | ||
| 103 | +###################### | ||
| 104 | +*.jar | ||
| 105 | +*.war | ||
| 106 | +*.ear | ||
| 107 | +*.db | ||
| 108 | + | ||
| 109 | +###################### | ||
| 162 | # Windows | 110 | # Windows |
| 111 | +###################### | ||
| 112 | +# Windows image file caches | ||
| 163 | Thumbs.db | 113 | Thumbs.db |
| 114 | + | ||
| 115 | +# Folder com.migu.oes.power.config file | ||
| 164 | Desktop.ini | 116 | Desktop.ini |
| 165 | 117 | ||
| 166 | -# macOS | 118 | +###################### |
| 119 | +# Mac OSX | ||
| 120 | +###################### | ||
| 167 | .DS_Store | 121 | .DS_Store |
| 168 | -.AppleDouble | ||
| 169 | -.LSOverride | ||
| 170 | - | ||
| 171 | -# Linux | 122 | +.svn |
| 123 | + | ||
| 124 | +# Thumbnails | ||
| 125 | +._* | ||
| 126 | + | ||
| 127 | +# Files that might appear on external disk | ||
| 128 | +.Spotlight-V100 | ||
| 129 | +.Trashes | ||
| 130 | + | ||
| 131 | +###################### | ||
| 132 | +# Directories | ||
| 133 | +###################### | ||
| 134 | +/bin/ | ||
| 135 | +/deploy/ | ||
| 136 | + | ||
| 137 | +###################### | ||
| 138 | +# Logs | ||
| 139 | +###################### | ||
| 140 | +*.log* | ||
| 141 | + | ||
| 142 | +###################### | ||
| 143 | +# Others | ||
| 144 | +###################### | ||
| 145 | +*.class | ||
| 146 | +*.*~ | ||
| 172 | *~ | 147 | *~ |
| 173 | -.nfs* | ||
| 148 | +.merge_file* | ||
| 149 | + | ||
| 150 | +###################### | ||
| 151 | +# Gradle Wrapper | ||
| 152 | +###################### | ||
| 153 | +!gradle/wrapper/gradle-wrapper.jar | ||
| 154 | + | ||
| 155 | +###################### | ||
| 156 | +# Maven Wrapper | ||
| 157 | +###################### | ||
| 158 | +!.mvn/wrapper/maven-wrapper.jar | ||
| 159 | + | ||
| 160 | +###################### | ||
| 161 | +# ESLint | ||
| 162 | +###################### | ||
| 163 | +.eslintcache | ||
| 164 | +*.lst | ||
| 165 | +oes-nryy-vo/target/maven-archiver/pom.properties | ||
| 166 | +oes-nryy-dataservice/target/maven-archiver/pom.properties | ||
| 167 | +oes-nryy-base/target/maven-archiver/pom.properties | ||
| 168 | +oes-nryy-copyright/bin/.gitkeep | ||
| 169 | +oes-nryy-copyright/bin/cbak | ||
| 170 | +*.original | ||
| 171 | +*.gz | ||
| 172 | +oes-nryy-tag/target/oes-nryy-tag-1.0.0/label/bin/service.sh | ||
| 173 | +oes-nryy-tag/target/maven-archiver/pom.properties | ||
| 174 | +oes-nryy-data-trans/target/maven-archiver/pom.properties | ||
| 175 | +oes-nryy-tag/target/oes-nryy-tag-1.0.0/label/bin/cbak | ||
| 176 | + | ||
| 177 | + | ||
| 178 | +###################### | ||
| 179 | +# pp-scene-det | ||
| 180 | +###################### | ||
| 181 | +__pycache__/ | ||
| 182 | + | ||
| 183 | +###################### | ||
| 184 | +# live-stream-ai-task-workline | ||
| 185 | +###################### | ||
| 186 | +modules/basketball/weights | ||
| 187 | +modules/singer/weights | ||
| 188 | +weights/ | ||
| 189 | +weights1/ | ||
| 190 | +files/ | ||
| 191 | +*.log | ||
| 192 | +*.log.* | ||
| 193 | +cache_data/ | ||
| 194 | +data/ | ||
| 195 | +test/ | ||
| 196 | +# Media files | ||
| 197 | +*.jpg | ||
| 198 | +*.jpeg | ||
| 199 | +*.png | ||
| 200 | +*.gif | ||
| 201 | +*.mp4 | ||
| 202 | +*.avi | ||
| 203 | +*.mov | ||
| 204 | +*.mkv | ||
| 205 | + | ||
| 206 | +# OS | ||
| 207 | +.DS_Store | ||
| 208 | +Thumbs.db | ||
| 209 | + |
README.md
0 → 100644
app/__init__.py
0 → 100644
app/api/__init__.py
0 → 100644
app/api/resp_bean.py
0 → 100644
| 1 | +from pydantic import BaseModel, Field | ||
| 2 | +from typing import Generic, TypeVar, Optional | ||
| 3 | + | ||
| 4 | +T = TypeVar('T') | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +class RespBean(BaseModel, Generic[T]): | ||
| 8 | + code: str = Field(default="success", description="code") | ||
| 9 | + message: str = Field(default="success", description='message') | ||
| 10 | + data: Optional[T] = Field(description="数据", default=None) | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +def error(message): | ||
| 14 | + return RespBean(code="error", message=message, data=None) | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +def success(data=None): | ||
| 18 | + return RespBean(data=data) |
app/api/router.py
0 → 100644
app/core/__init__.py
0 → 100644
app/core/cfg/config.yaml
0 → 100644
app/core/config.py
0 → 100644
app/core/exceptions.py
0 → 100644
| 1 | +"""自定义异常模块.""" | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +class EmbeddingServiceError(Exception): | ||
| 5 | + """Embedding服务基础异常.""" | ||
| 6 | + | ||
| 7 | + def __init__(self, message: str, code: str = "EMBEDDING_ERROR"): | ||
| 8 | + self.message = message | ||
| 9 | + self.code = code | ||
| 10 | + super().__init__(self.message) | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +class EmbeddingAPIError(EmbeddingServiceError): | ||
| 14 | + """Embedding API调用异常.""" | ||
| 15 | + | ||
| 16 | + def __init__(self, message: str, status_code: int = 500): | ||
| 17 | + super().__init__(message, code="EMBEDDING_API_ERROR") | ||
| 18 | + self.status_code = status_code | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +class DatabaseError(EmbeddingServiceError): | ||
| 22 | + """数据库操作异常.""" | ||
| 23 | + | ||
| 24 | + def __init__(self, message: str): | ||
| 25 | + super().__init__(message, code="DATABASE_ERROR") | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +class NotFoundError(EmbeddingServiceError): | ||
| 29 | + """资源不存在异常.""" | ||
| 30 | + | ||
| 31 | + def __init__(self, message: str): | ||
| 32 | + super().__init__(message, code="NOT_FOUND") | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +class ValidationError(EmbeddingServiceError): | ||
| 36 | + """参数校验异常.""" | ||
| 37 | + | ||
| 38 | + def __init__(self, message: str): | ||
| 39 | + super().__init__(message, code="VALIDATION_ERROR") |
app/db/__init__.py
0 → 100644
| 1 | +"""数据库模块,提供统一的向量数据库抽象层. | ||
| 2 | + | ||
| 3 | +支持Elasticsearch和PostgreSQL两种后端,通过配置自动切换。 | ||
| 4 | +使用示例: | ||
| 5 | + from app.db import db_client | ||
| 6 | + | ||
| 7 | + # 连接数据库 | ||
| 8 | + await db_client.connect() | ||
| 9 | + | ||
| 10 | + # 创建集合 | ||
| 11 | + await db_client.create_collection("face_vectors", vector_dim=512) | ||
| 12 | + | ||
| 13 | + # 插入数据 | ||
| 14 | + await db_client.insert("face_vectors", "user_001", vector, {"name": "张三"}) | ||
| 15 | + | ||
| 16 | + # 向量搜索 | ||
| 17 | + results = await db_client.search("face_vectors", query_vector, top_k=10) | ||
| 18 | + | ||
| 19 | + # 断开连接 | ||
| 20 | + await db_client.disconnect() | ||
| 21 | +""" | ||
| 22 | + | ||
| 23 | +from typing import Optional | ||
| 24 | + | ||
| 25 | +from app.core.config import settings | ||
| 26 | + | ||
| 27 | +es_client = None | ||
| 28 | +pg_client = None | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +async def connect(): | ||
| 32 | + if settings.db_established: | ||
| 33 | + from .es_client import get_es_client | ||
| 34 | + global es_client | ||
| 35 | + es_client = get_es_client() | ||
| 36 | + await es_client.connect() | ||
| 37 | + if settings.db_postgres_enable: | ||
| 38 | + from .postgres_client import get_pg_client | ||
| 39 | + global pg_client | ||
| 40 | + pg_client = get_pg_client() | ||
| 41 | + await pg_client.connect() | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +async def disconnect(): | ||
| 45 | + global es_client, pg_client | ||
| 46 | + if es_client is not None: | ||
| 47 | + await es_client.disconnect() | ||
| 48 | + es_client = None | ||
| 49 | + if pg_client is not None: | ||
| 50 | + await pg_client.disconnect() |
app/db/base.py
0 → 100644
| 1 | +"""数据库抽象基类,定义统一的CRUD接口.""" | ||
| 2 | + | ||
| 3 | +from abc import ABC, abstractmethod | ||
| 4 | +from typing import Any, Dict, List, Optional | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +class VectorDBClient(ABC): | ||
| 8 | + """向量数据库客户端抽象基类. | ||
| 9 | + | ||
| 10 | + 定义了统一的向量数据CRUD接口,支持Elasticsearch和PostgreSQL等后端。 | ||
| 11 | + """ | ||
| 12 | + | ||
| 13 | + @abstractmethod | ||
| 14 | + async def connect(self) -> None: | ||
| 15 | + """建立数据库连接.""" | ||
| 16 | + pass | ||
| 17 | + | ||
| 18 | + @abstractmethod | ||
| 19 | + async def disconnect(self) -> None: | ||
| 20 | + """断开数据库连接.""" | ||
| 21 | + pass | ||
| 22 | + | ||
| 23 | + @abstractmethod | ||
| 24 | + async def health_check(self) -> bool: | ||
| 25 | + """检查数据库连接健康状态. | ||
| 26 | + | ||
| 27 | + Returns: | ||
| 28 | + bool: 连接正常返回True,否则返回False | ||
| 29 | + """ | ||
| 30 | + pass |
app/db/es_client.py
0 → 100644
| 1 | +"""Elasticsearch向量数据库客户端实现.""" | ||
| 2 | + | ||
| 3 | +from typing import Any, Dict, List, Optional | ||
| 4 | + | ||
| 5 | +from app.core.config import settings | ||
| 6 | +from app.db.base import VectorDBClient | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +class ElasticsearchClient(VectorDBClient): | ||
| 10 | + """Elasticsearch向量数据库客户端. | ||
| 11 | + | ||
| 12 | + 使用Elasticsearch的dense_vector类型存储向量,支持向量相似度搜索。 | ||
| 13 | + """ | ||
| 14 | + | ||
| 15 | + def __init__(self, es_url: Optional[str] = None): | ||
| 16 | + """初始化ES客户端. | ||
| 17 | + | ||
| 18 | + Args: | ||
| 19 | + es_url: Elasticsearch连接URL,默认从配置读取 | ||
| 20 | + """ | ||
| 21 | + self.es_url = es_url or settings.get("db_es_url", "http://localhost:9200") | ||
| 22 | + self._client: Optional[Any] = None | ||
| 23 | + | ||
| 24 | + async def connect(self) -> None: | ||
| 25 | + """建立ES连接.""" | ||
| 26 | + from elasticsearch import AsyncElasticsearch | ||
| 27 | + | ||
| 28 | + self._client = AsyncElasticsearch([self.es_url]) | ||
| 29 | + # 验证连接 | ||
| 30 | + await self._client.ping() | ||
| 31 | + | ||
| 32 | + async def disconnect(self) -> None: | ||
| 33 | + """断开ES连接.""" | ||
| 34 | + if self._client: | ||
| 35 | + await self._client.close() | ||
| 36 | + self._client = None | ||
| 37 | + | ||
| 38 | + async def health_check(self) -> bool: | ||
| 39 | + """检查ES连接健康状态.""" | ||
| 40 | + if not self._client: | ||
| 41 | + return False | ||
| 42 | + try: | ||
| 43 | + return await self._client.ping() | ||
| 44 | + except Exception: | ||
| 45 | + return False | ||
| 46 | +_es_client: Optional[ElasticsearchClient] = None | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +def get_es_client() -> ElasticsearchClient: | ||
| 50 | + """获取全局ES客户端实例(单例模式). | ||
| 51 | + | ||
| 52 | + Returns: | ||
| 53 | + ElasticsearchClient: ES客户端实例 | ||
| 54 | + """ | ||
| 55 | + global _es_client | ||
| 56 | + if _es_client is None: | ||
| 57 | + _es_client = ElasticsearchClient() | ||
| 58 | + return _es_client |
app/db/postgres_client.py
0 → 100644
| 1 | +"""PostgreSQL向量数据库客户端实现.""" | ||
| 2 | + | ||
| 3 | +import json | ||
| 4 | +from typing import Any, Dict, List, Optional | ||
| 5 | + | ||
| 6 | +from app.db.base import VectorDBClient | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +class PostgresClient(VectorDBClient): | ||
| 10 | + """PostgreSQL向量数据库客户端. | ||
| 11 | + | ||
| 12 | + 使用pgvector扩展存储向量,支持向量相似度搜索。 | ||
| 13 | + 需要安装pgvector扩展: CREATE EXTENSION IF NOT EXISTS vector; | ||
| 14 | + """ | ||
| 15 | + | ||
| 16 | + def __init__(self, pg_url: Optional[str] = None): | ||
| 17 | + """初始化PG客户端. | ||
| 18 | + | ||
| 19 | + Args: | ||
| 20 | + pg_url: PostgreSQL连接URL,默认从配置读取 | ||
| 21 | + """ | ||
| 22 | + self.pg_url = pg_url | ||
| 23 | + self._pool: Optional[Any] = None | ||
| 24 | + | ||
| 25 | + async def connect(self) -> None: | ||
| 26 | + """建立PG连接池.""" | ||
| 27 | + import psycopg | ||
| 28 | + from psycopg_pool import AsyncConnectionPool | ||
| 29 | + | ||
| 30 | + self._pool = AsyncConnectionPool( | ||
| 31 | + conninfo=self.pg_url, | ||
| 32 | + min_size=1, | ||
| 33 | + max_size=10, | ||
| 34 | + ) | ||
| 35 | + | ||
| 36 | + # 验证连接并启用pgvector | ||
| 37 | + async with self._pool.connection() as conn: | ||
| 38 | + await conn.execute("CREATE EXTENSION IF NOT EXISTS vector") | ||
| 39 | + await conn.commit() | ||
| 40 | + | ||
| 41 | + async def disconnect(self) -> None: | ||
| 42 | + """断开PG连接.""" | ||
| 43 | + if self._pool: | ||
| 44 | + await self._pool.close() | ||
| 45 | + self._pool = None | ||
| 46 | + | ||
| 47 | + async def health_check(self) -> bool: | ||
| 48 | + """检查PG连接健康状态.""" | ||
| 49 | + if not self._pool: | ||
| 50 | + return False | ||
| 51 | + try: | ||
| 52 | + async with self._pool.connection() as conn: | ||
| 53 | + cursor = await conn.execute("SELECT 1") | ||
| 54 | + result = await cursor.fetchone() | ||
| 55 | + return result is not None and result[0] == 1 | ||
| 56 | + except Exception: | ||
| 57 | + return False | ||
| 58 | + | ||
| 59 | + | ||
| 60 | +# 全局PG客户端实例 | ||
| 61 | +_pg_client: Optional[PostgresClient] = None | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +def get_pg_client() -> PostgresClient: | ||
| 65 | + """获取全局PG客户端实例(单例模式). | ||
| 66 | + | ||
| 67 | + Returns: | ||
| 68 | + PostgresClient: PG客户端实例 | ||
| 69 | + """ | ||
| 70 | + global _pg_client | ||
| 71 | + if _pg_client is None: | ||
| 72 | + _pg_client = PostgresClient() | ||
| 73 | + return _pg_client |
app/main.py
0 → 100644
| 1 | +"""FastAPI应用入口.""" | ||
| 2 | + | ||
| 3 | +from contextlib import asynccontextmanager | ||
| 4 | +from typing import AsyncGenerator | ||
| 5 | + | ||
| 6 | +from fastapi import FastAPI | ||
| 7 | +from fastapi.middleware.cors import CORSMiddleware | ||
| 8 | + | ||
| 9 | +from app.api.router import api_router | ||
| 10 | +from app.core.exceptions import EmbeddingServiceError | ||
| 11 | +from app.core.config import settings | ||
| 12 | +from app.db import connect as db_client_connect, disconnect as db_client_disconnect | ||
| 13 | + | ||
| 14 | +@asynccontextmanager | ||
| 15 | +async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: | ||
| 16 | + """应用生命周期管理.""" | ||
| 17 | + # 启动时建立数据库连接 | ||
| 18 | + try: | ||
| 19 | + await db_client_connect() | ||
| 20 | + db_type = settings.get("db_type", "es") | ||
| 21 | + print(f"✓ {db_type.upper()} database connected") | ||
| 22 | + except Exception as e: | ||
| 23 | + print(f"✗ Failed to connect to database: {e}") | ||
| 24 | + raise | ||
| 25 | + | ||
| 26 | + yield | ||
| 27 | + | ||
| 28 | + # 关闭时断开数据库连接 | ||
| 29 | + try: | ||
| 30 | + await db_client_disconnect() | ||
| 31 | + db_type = settings.get("db_type", "es") | ||
| 32 | + print(f"✓ {db_type.upper()} database disconnected") | ||
| 33 | + except Exception as e: | ||
| 34 | + print(f"✗ Error disconnecting from database: {e}") | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +def create_app() -> FastAPI: | ||
| 38 | + """创建FastAPI应用.""" | ||
| 39 | + | ||
| 40 | + app = FastAPI( | ||
| 41 | + title=settings.app_name, | ||
| 42 | + version=settings.app_version, | ||
| 43 | + description="基于向量的CRUD服务,支持多种Embedding场景", | ||
| 44 | + lifespan=lifespan, | ||
| 45 | + docs_url="/docs", | ||
| 46 | + redoc_url="/redoc", | ||
| 47 | + ) | ||
| 48 | + | ||
| 49 | + # 配置CORS | ||
| 50 | + app.add_middleware( | ||
| 51 | + CORSMiddleware, | ||
| 52 | + allow_origins=["*"], | ||
| 53 | + allow_credentials=True, | ||
| 54 | + allow_methods=["*"], | ||
| 55 | + allow_headers=["*"], | ||
| 56 | + ) | ||
| 57 | + | ||
| 58 | + # 注册路由 | ||
| 59 | + app.include_router(api_router) | ||
| 60 | + | ||
| 61 | + # 异常处理 | ||
| 62 | + @app.exception_handler(EmbeddingServiceError) | ||
| 63 | + async def embedding_service_exception_handler(request, exc): | ||
| 64 | + from fastapi.responses import JSONResponse | ||
| 65 | + | ||
| 66 | + return JSONResponse( | ||
| 67 | + status_code=500, | ||
| 68 | + content={"code": exc.code, "message": exc.message}, | ||
| 69 | + ) | ||
| 70 | + | ||
| 71 | + # @app.get("/health", tags=["健康检查"]) | ||
| 72 | + # async def health_check(): | ||
| 73 | + # """健康检查端点.""" | ||
| 74 | + # db_healthy = await db_client.health_check() | ||
| 75 | + # db_type = settings.get("db_type", "es") | ||
| 76 | + # return { | ||
| 77 | + # "status": "healthy" if db_healthy else "unhealthy", | ||
| 78 | + # "database": { | ||
| 79 | + # "type": db_type, | ||
| 80 | + # "status": "connected" if db_healthy else "disconnected", | ||
| 81 | + # }, | ||
| 82 | + # } | ||
| 83 | + | ||
| 84 | + @app.get("/", tags=["根路径"]) | ||
| 85 | + async def root(): | ||
| 86 | + """根路径.""" | ||
| 87 | + return { | ||
| 88 | + "name": settings.app_name, | ||
| 89 | + "version": settings.app_version, | ||
| 90 | + "docs": "/docs", | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + return app | ||
| 94 | + | ||
| 95 | + | ||
| 96 | +# 创建应用实例 | ||
| 97 | +app = create_app() | ||
| 98 | + | ||
| 99 | +if __name__ == "__main__": | ||
| 100 | + import uvicorn | ||
| 101 | + | ||
| 102 | + uvicorn.run( | ||
| 103 | + "app.main:app", | ||
| 104 | + host='0.0.0.0', | ||
| 105 | + port=settings.port, | ||
| 106 | + reload=settings.debug, | ||
| 107 | + ) |
app/modules/__init__.py
0 → 100644
app/modules/face/__init__.py
0 → 100644
app/modules/face/es_db.py
0 → 100644
app/modules/face/router.py
0 → 100644
app/modules/face/service.py
0 → 100644
| 1 | [project] | 1 | [project] |
| 2 | name = "aigc-embedding-service" | 2 | name = "aigc-embedding-service" |
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | -description = "Add your description here" | 4 | +description = "基于向量的CRUD服务,支持多种Embedding场景" |
| 5 | requires-python = ">=3.12" | 5 | requires-python = ">=3.12" |
| 6 | -dependencies = [] | 6 | +dependencies = [ |
| 7 | + "fastapi>=0.115.0", | ||
| 8 | + "uvicorn[standard]>=0.32.0", | ||
| 9 | + "pydantic>=2.9.0", | ||
| 10 | + "pydantic-settings>=2.6.0", | ||
| 11 | + "httpx>=0.27.0", | ||
| 12 | + "python-multipart>=0.0.12", | ||
| 13 | + "aabd>=0.4.1", | ||
| 14 | + "path>=17.1.1", | ||
| 15 | + "omegaconf>=2.3.0", | ||
| 16 | +] | ||
| 17 | + | ||
| 18 | +[project.optional-dependencies] | ||
| 19 | +dev = [ | ||
| 20 | + "pytest>=8.3.0", | ||
| 21 | + "pytest-asyncio>=0.24.0", | ||
| 22 | + "black>=24.10.0", | ||
| 23 | + "ruff>=0.7.0", | ||
| 24 | + "mypy>=1.13.0", | ||
| 25 | +] | ||
| 26 | +es = [ | ||
| 27 | + "elasticsearch>=8.15.0", | ||
| 28 | +] | ||
| 29 | +postgres = [ | ||
| 30 | + "psycopg[binary,pool]>=3.2.0", | ||
| 31 | + "psycopg-pool>=3.3.0", | ||
| 32 | +] | ||
| 33 | + | ||
| 34 | +[tool.black] | ||
| 35 | +line-length = 100 | ||
| 36 | +target-version = ['py312'] | ||
| 37 | + | ||
| 38 | +[tool.ruff] | ||
| 39 | +line-length = 100 | ||
| 40 | +select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"] | ||
| 41 | +ignore = ["E501"] | ||
| 42 | + | ||
| 43 | +[tool.mypy] | ||
| 44 | +python_version = "3.12" | ||
| 45 | +warn_return_any = true | ||
| 46 | +warn_unused_configs = true | ||
| 47 | +disallow_untyped_defs = true | ||
| 48 | + | ||
| 49 | +[tool.pytest.ini_options] | ||
| 50 | +asyncio_mode = "auto" | ||
| 51 | +testpaths = ["tests"] | ||
| 52 | +python_files = ["test_*.py"] | ||
| 53 | +python_classes = ["Test*"] | ||
| 54 | +python_functions = ["test_*"] |
uv.lock
0 → 100644
This diff could not be displayed because it is too large.
-
Please register or login to post a comment