feat:1.移除所有明文服务密钥,采用环境变量方式读取

2.回调信息简化 \ stylist_agent_server.py中 一部分逻辑更新
This commit is contained in:
zcr
2025-12-16 17:29:05 +08:00
parent 46b96995f0
commit 3e70324261
15 changed files with 173 additions and 152 deletions

View File

@@ -1,2 +0,0 @@
GEMINI_API_KEY=AIzaSyAO4zXFke1bqyrXd9-RGfKJTLerwLSFKww
GOOGLE_APPLICATION_CREDENTIALS="/workspace/lc_stylist_agent/app/request.json"

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@ __pycache__/
data/ data/
.idea/ .idea/
*.log *.log
*.toml
.prod_env
google_application_credentials.json

View File

@@ -3,8 +3,8 @@ import os
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field from pydantic import Field
# ⚠️ 注意: 您需要安装 pydantic-settings: pip install pydantic-settings # ⚠️ 注意: 您需要安装 pydantic-settings: pip install pydantic-settings
DEBUG = os.environ.get("DEBUG", 1)
class Settings(BaseSettings): class Settings(BaseSettings):
@@ -35,13 +35,17 @@ class Settings(BaseSettings):
STYLIST_GUIDE_DIR: str = Field(default="/workspace/lc_stylist_agent/data/stylist_guide", description="风格指南文本目录") STYLIST_GUIDE_DIR: str = Field(default="/workspace/lc_stylist_agent/data/stylist_guide", description="风格指南文本目录")
# 向量数据库配置参数 # 向量数据库配置参数
if DEBUG == 1:
VECTOR_DB_DIR: str = Field(default="/workspace/lc_stylist_agent/db", description="向量数据库目录")
else:
VECTOR_DB_DIR: str = Field(default="/db", description="向量数据库目录") VECTOR_DB_DIR: str = Field(default="/db", description="向量数据库目录")
COLLECTION_NAME: str = Field(default="lc_clothing_embedding", description="向量数据库集合名称") COLLECTION_NAME: str = Field(default="lc_clothing_embedding", description="向量数据库集合名称")
EMBEDDING_MODEL_NAME: str = Field(default="openai/clip-vit-base-patch32", description="CLIP嵌入模型名称") EMBEDDING_MODEL_NAME: str = Field(default="openai/clip-vit-base-patch32", description="CLIP嵌入模型名称")
# minio配置
MINIO_URL: str = Field(default="", description="URL")
MINIO_ACCESS: str = Field(default="", description="ACCESS")
MINIO_SECRET: str = Field(default="", description="SECRET")
MINIO_SECURE: bool = Field(default=True, description="SECRET")
MINIO_LC_DATA_PATH: str = Field(default="", description="图片数据路径")
# 创建配置实例,供应用其他部分使用 # 创建配置实例,供应用其他部分使用
settings = Settings() settings = Settings()

View File

@@ -1,7 +1,7 @@
import logging.config import logging.config
import os import os
import litserve as ls import litserve as ls
from app.config import DEBUG, settings from app.config import settings
from app.server.ChatbotAgent.agent_server import LCAgent from app.server.ChatbotAgent.agent_server import LCAgent
from app.server.ChatbotAgent.chatbot_server import LCChatBot from app.server.ChatbotAgent.chatbot_server import LCChatBot
from app.server.ReFace.server import ReFace from app.server.ReFace.server import ReFace
@@ -21,7 +21,7 @@ logging.config.dictConfig(LOGGER_CONFIG_DICT)
# STEP 2: START THE SERVER # STEP 2: START THE SERVER
if __name__ == "__main__": if __name__ == "__main__":
logger.info(f"DEBUG -> :{DEBUG}") logger.info(f"运行环境 1表示本地运行0表示生产环境运行 -> :{settings.LOCAL}")
logger.info(f"VECTOR_DB_DIR -> :{settings.VECTOR_DB_DIR}") logger.info(f"VECTOR_DB_DIR -> :{settings.VECTOR_DB_DIR}")
chat_boot_api = LCChatBot(enable_async=True, stream=True, api_path='/api/v1/chatbot') chat_boot_api = LCChatBot(enable_async=True, stream=True, api_path='/api/v1/chatbot')
agent_api = LCAgent(enable_async=True, api_path='/api/v1/agent') agent_api = LCAgent(enable_async=True, api_path='/api/v1/agent')

View File

@@ -1,13 +0,0 @@
{
"type": "service_account",
"project_id": "aida-461108",
"private_key_id": "b4afaabebb84da24502b318a5fa175f1dc5c096a",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCmk7LKrp8g9yD1\nWmF+mY2qHCEZ/5aIx6QRh0QoVPBL7Yi7ce009QxaE8fu8+QMgg8l3xMreXvgpt56\noFnVwpFusLjSdjgoFluElM2hYxXlO9q8cbBoU2nehOBLLJzGzkodT7xu/BOjNvKC\n//aTbjtJyk8Kj+ENa0/dPaUZs/PCtQqpAu8ag5nXrordVWfO0K25EjeYyoba35zk\nPp2fBi8KALZZI5Xfd2z9++K0K2mWWIMJic30idHvquj0WxlTRK2Pq8BmJXCQpJIi\nQ5E4egue16BfKjrF0Kxkpqd1RmdlEmaSKbbkZXe2z4jg0qknESRFOmRy8C3LnaB2\nHHJWLYM3AgMBAAECggEACUdroOQJSTTQSS/iWRhZ+S0yoC10nTnsZxg527qfiBs7\nOqB7WNqC+Ew8dDsca6CdvLuoaGDkCFJDTQwRn66u8JOM4sG4bxiPuzBEJBv45EQT\n8zCsuvhVNWgBdoPjAnq19jFdixvPnDqQrRYaY4FdxsaA5f24c57pW/xLGMYawLBt\n9RJZSuWmJdzKG1i5W8a8+4f/seNtuo2MtXU3mPJZPqRWPXTAZeaQPM/57ZQ+kzig\nOkAbQZNRmt1yPCjPCQD8vc8yCBMmjus/rlHXD/L7okYUlVZkob5I3FBrLl+ZyIXS\nqxEsBLBwRW3w8WbX+ZSVciQ72JK68W7LnOHSAENmAQKBgQDgBTCqp87KGLWVPb8w\nK+s1Sfh+nM3M4AlbLdcGBs1JCoddF6pAeY4wpf/ow1Tm4rqEuCYzMClPwxvkue+D\nY7lCQgy2FK3ahUzn8oVmvEPD/YPAojDSY3bH0lquHuS6oVKk834JUykButaAU3XY\nvUGNQuKdLKAeQRT8Q6um4m+EYQKBgQC+Wz6nYESKH6GiNnuFTH8hIkThPlbi4wua\nU1kGnPKe3ouE4zRLfPwQ6RRf1slQ/2hFLOatiTLYUgZWZQeBPSWp2EjYcOSzob+7\n11+KqeIRCD5DKxgf0cjJdihK9AM639OKlH2NvZ2507TksdeTPDzdaOMLwLWKexP5\nlYrdob0ulwKBgD81t7Gvf83Ogw4FSjkRa2Cx6ofvPrKcVIeBu7ZbnPkLG37M+qEO\nq2xWqorG8uHi/7YLL9wprr5u0yQKwuZT8SYc9PE7jIKoMjcQW0vNu2FF2zMzkIsM\nvatMU4Hl/awbcPJSMjH3YQ635WZ4Jjxtyl1NjhvDR7rBqmYzwe9o3QaBAoGANhPB\n1tbYYczepDCKIrI6o3US0FJfaJFLqInpDqHjoxJh3FyXbKKTEVLFwPxJsML+IjjB\nR6dkVGPo/P4yhZqTao7REvvvXMCksX5b3A6q9F+9IGPLtK5qNiFlDPYJPN59QC8z\nA+NMPZBRIW8MaP2B5Px5E8upRy/z2sGK86+RCP0CgYATGs75F97q+Zf8q+Pe3Nsb\ngqmhLoI3PZUSWgBcQgNF4nyCZceUrEl72wKO/NWLgxqQPtlra187ce69g7qARHLb\ntHq80nb0f7lil74B6+OlyNNO1htWA90fmGR2s16Mt0BwJRT+/EFuNqbJIUSLxKiW\nqlXBUbmHHzamo5DPYL8S/w==\n-----END PRIVATE KEY-----\n",
"client_email": "aida-239@aida-461108.iam.gserviceaccount.com",
"client_id": "103102077955178349079",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/aida-239%40aida-461108.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

View File

@@ -38,6 +38,7 @@ class OccasionEnum(str, Enum):
SKI_SNOW_MOUNTAIN = "Ski / Snow / Mountain" SKI_SNOW_MOUNTAIN = "Ski / Snow / Mountain"
GARDEN_PARTY_DAYTIME = "Garden Party / Daytime Event" GARDEN_PARTY_DAYTIME = "Garden Party / Daytime Event"
class StylistResponse(BaseModel): class StylistResponse(BaseModel):
occasions: List[OccasionEnum] = Field( occasions: List[OccasionEnum] = Field(
description="A list of **applicable** occasions that are most strongly implied or explicitly requested by the user's conversation history. These occasions are used later in item retrieval for filtering and must strictly match the predefined OccasionEnum list." description="A list of **applicable** occasions that are most strongly implied or explicitly requested by the user's conversation history. These occasions are used later in item retrieval for filtering and must strictly match the predefined OccasionEnum list."
@@ -55,6 +56,7 @@ class AgentRequestModel(BaseModel):
batch_sources: List[str] batch_sources: List[str]
callback_url: str callback_url: str
gender: str gender: str
is_first_request: bool
class LCAgent(ls.LitAPI): class LCAgent(ls.LitAPI):
@@ -118,7 +120,8 @@ class LCAgent(ls.LitAPI):
user_id=request.user_id, user_id=request.user_id,
gender=request.gender, gender=request.gender,
callback_url=request.callback_url, callback_url=request.callback_url,
outfit_ids=outfit_ids outfit_ids=outfit_ids,
is_first_request=request.is_first_request
) )
logger.info("--- Final Recommendation Results ---") logger.info("--- Final Recommendation Results ---")
for i, path in enumerate(recommendation_results.get("successful_outfits", [])): for i, path in enumerate(recommendation_results.get("successful_outfits", [])):
@@ -171,7 +174,8 @@ class LCAgent(ls.LitAPI):
user_id: str = "test", user_id: str = "test",
gender: str = "male", gender: str = "male",
callback_url: str = None, callback_url: str = None,
outfit_ids=None outfit_ids=None,
is_first_request=False
): ):
""" """
基于用户的对话历史和需求,推荐一套搭配。 基于用户的对话历史和需求,推荐一套搭配。
@@ -191,6 +195,18 @@ class LCAgent(ls.LitAPI):
stylist_agent_kwages['stylist_name'] = stylist_name stylist_agent_kwages['stylist_name'] = stylist_name
stylist_agent_kwages['gender'] = gender stylist_agent_kwages['gender'] = gender
agent = AsyncStylistAgent(**stylist_agent_kwages) agent = AsyncStylistAgent(**stylist_agent_kwages)
if is_first_request:
# 第一套搭配使用快速方法 一次跑出所有单品
task = agent.run_quick_batch_styling(
request_summary=request_summary,
occasions=occasions,
start_outfit=start_outfit,
batch_sources=batch_sources,
user_id=user_id,
callback_url=callback_url,
)
else:
# 后续
task = agent.run_iterative_styling( task = agent.run_iterative_styling(
request_summary=request_summary, request_summary=request_summary,
occasions=occasions, occasions=occasions,
@@ -232,7 +248,7 @@ class LCAgent(ls.LitAPI):
stylist_agent_kwages['stylist_name'] = stylist_name stylist_agent_kwages['stylist_name'] = stylist_name
stylist_agent_kwages['gender'] = gender stylist_agent_kwages['gender'] = gender
agent = AsyncStylistAgent(**stylist_agent_kwages) agent = AsyncStylistAgent(**stylist_agent_kwages)
new_task = agent.run_iterative_styling( new_task = agent.run_quick_batch_styling(
request_summary=request_summary, request_summary=request_summary,
occasions=occasions, occasions=occasions,
start_outfit=start_outfit, start_outfit=start_outfit,
@@ -288,7 +304,7 @@ if __name__ == "__main__":
# 2. 准备请求数据 # 2. 准备请求数据
import json import json
stylist_agent_kwages = agent_api.stylist_agent_kwages.copy() stylist_agent_kwages = agent_api.stylist_agent_kwages.copy()
with open("./data/2025_q4/request_test.json", "r") as f: with open("/mnt/data/workspace/Code/lc_stylist_agent/data/2025_q4/request_test.json", "r") as f:
request_data = json.load(f) request_data = json.load(f)
tasks_with_metadata = [] tasks_with_metadata = []
@@ -300,14 +316,14 @@ if __name__ == "__main__":
stylist_agent_kwages['stylist_name'] = stylist_name stylist_agent_kwages['stylist_name'] = stylist_name
stylist_agent_kwages['gender'] = "female" stylist_agent_kwages['gender'] = "female"
agent = AsyncStylistAgent(**stylist_agent_kwages) agent = AsyncStylistAgent(**stylist_agent_kwages)
coro = agent.run_iterative_styling( # coro = agent.run_iterative_styling(
# coro = agent.run_quick_batch_styling( coro = agent.run_quick_batch_styling(
request_summary=request_summary, request_summary=request_summary,
occasions=occasions, occasions=occasions,
start_outfit=[], start_outfit=[],
batch_sources=["2025_q4"], batch_sources=["2025_q4"],
user_id=test_content['test_case_id'], user_id=test_content['test_case_id'],
callback_url="http://mock-callback.com/result", callback_url="http://18.167.251.121:10095",
) )
# 记录任务开始前的单调时间,并将元数据添加到列表中 # 记录任务开始前的单调时间,并将元数据添加到列表中
description = f"Batch mode - Case {test_content['test_case_id']} - Stylist {stylist_name}" description = f"Batch mode - Case {test_content['test_case_id']} - Stylist {stylist_name}"
@@ -331,6 +347,7 @@ if __name__ == "__main__":
print(f"Average time consumption is {sum(time_samples) / len(time_samples)}") print(f"Average time consumption is {sum(time_samples) / len(time_samples)}")
try: try:
# 使用 asyncio.run() 来执行顶层异步函数 # 使用 asyncio.run() 来执行顶层异步函数
asyncio.run(test()) asyncio.run(test())

View File

@@ -131,7 +131,6 @@ class AsyncStylistAgent:
""" """
if not self.outfit_items: if not self.outfit_items:
return "", None return "", None
merged_image = merge_images_to_square(self.outfit_items, max_len=9, add_text=False) merged_image = merge_images_to_square(self.outfit_items, max_len=9, add_text=False)
image_bytes_io = io.BytesIO() image_bytes_io = io.BytesIO()
image_format = 'JPEG' image_format = 'JPEG'
@@ -146,7 +145,8 @@ class AsyncStylistAgent:
f.write(image_bytes) f.write(image_bytes)
return local_file_path, image_bytes return local_file_path, image_bytes
else: else:
blob_name = f"lc_stylist_agent_outfit_items/{user_id}/{file_name}.jpg" # minio文件地址需保持变动否则前端缓存导致无法更新图片
blob_name = f"lc_stylist_agent_outfit_items/{user_id}/{file_name}-{len(self.outfit_items)}.jpg"
responses = oss_upload_image(oss_client=minio_client, bucket=self.minio_bucket, object_name=blob_name, image_bytes=image_bytes) responses = oss_upload_image(oss_client=minio_client, bucket=self.minio_bucket, object_name=blob_name, image_bytes=image_bytes)
minio_path = f"{responses.bucket_name}/{responses.object_name}" minio_path = f"{responses.bucket_name}/{responses.object_name}"
return minio_path, image_bytes return minio_path, image_bytes
@@ -242,15 +242,24 @@ class AsyncStylistAgent:
def post_operation(self, status: str, message: str, callback_url: str, img_path: str): def post_operation(self, status: str, message: str, callback_url: str, img_path: str):
"""处理完成后的回调操作。""" """处理完成后的回调操作。"""
if settings.LOCAL == 0: if settings.LOCAL == 0:
# 生产回调请求数据处理
items = []
for item in self.outfit_items:
items.append(
{
"item_id": item['item_id'],
"category": item['subcategory']
}
)
response_data = { response_data = {
'items': deepcopy(self.outfit_items), 'items': items,
'status': status, 'status': status,
'message': message, # 'message': message,
'path': img_path, 'path': img_path,
'outfit_id': self.outfit_id 'outfit_id': self.outfit_id
} }
response = post_request(url=callback_url, data=json.dumps(response_data), headers=self.headers) response = post_request(url=callback_url, data=json.dumps(response_data), headers=self.headers)
logger.info(f"request data {response_data} | JAVA callback info -> status:{response.status_code} | message:{response.text}") logger.info(f"request data {json.dumps(response_data, ensure_ascii=False, indent=2)} | JAVA callback info -> status:{response.status_code} | message:{response.text}")
return response_data return response_data
else: else:
return {} return {}
@@ -350,7 +359,6 @@ class AsyncStylistAgent:
) )
print(f"Stage {current_category.upper()}, Step {recommend_timestep}: {gemini_data}, found item: {new_item['item_id']}") print(f"Stage {current_category.upper()}, Step {recommend_timestep}: {gemini_data}, found item: {new_item['item_id']}")
async def _execute_batch_recommendation( async def _execute_batch_recommendation(
self, self,
current_category: str, # this can be any category or all current_category: str, # this can be any category or all
@@ -362,8 +370,9 @@ class AsyncStylistAgent:
url: str url: str
): ):
user_input = self._build_user_input(current_category, existing_subcategories=", ".join([x['subcategory'] for x in self.outfit_items])) user_input = self._build_user_input(current_category, existing_subcategories=", ".join([x['subcategory'] for x in self.outfit_items]))
# 合并图片
merged_image_path, image_bytes = await self._merge_images(self.outfit_id, user_id, self.stylist_name) merged_image_path, image_bytes = await self._merge_images(self.outfit_id, user_id, self.stylist_name)
# 调用Gemini API
gemini_response_text = await self._call_gemini( gemini_response_text = await self._call_gemini(
user_input, user_input,
user_id, user_id,
@@ -372,9 +381,11 @@ class AsyncStylistAgent:
image_bytes, image_bytes,
system_prompt system_prompt
) )
# 解析响应
gemini_data = self._parse_gemini_response(gemini_response_text) gemini_data = self._parse_gemini_response(gemini_response_text)
recommended_items = gemini_data.get('recommended_items', []) recommended_items = gemini_data.get('recommended_items', [])
reason = gemini_data.get('reason', '') reason = gemini_data.get('reason', '')
if not recommended_items or not isinstance(recommended_items, List): if not recommended_items or not isinstance(recommended_items, List):
print("No recommended item from Gemini, terminating process.") print("No recommended item from Gemini, terminating process.")
self.post_operation( self.post_operation(
@@ -411,11 +422,14 @@ class AsyncStylistAgent:
print(f"Item {idx + 1}: ({subcategory}) {rec_item}, found item: {new_item}") print(f"Item {idx + 1}: ({subcategory}) {rec_item}, found item: {new_item}")
return reason return reason
async def run_iterative_styling(self, request_summary, occasions, start_outfit: Optional[List] = None, batch_sources: List = [], user_id="test", callback_url=""):
async def run_iterative_styling(self, request_summary, occasions, start_outfit=[], batch_sources=[], user_id="test", callback_url=""):
start_time = time.monotonic() start_time = time.monotonic()
STAGES = ['clothing', 'shoes', 'bags'] STAGES = ['clothing', 'shoes', 'bags']
self.outfit_items = start_outfit # 深拷贝start_outfit 避免实例之间的参数泄漏 确保每个实例都有自己的 start_outfit 副本
if start_outfit is None:
self.outfit_items = []
else:
self.outfit_items = deepcopy(start_outfit)
stylist_guide, accessories_guide = self._load_style_guide(self.stylist_name) stylist_guide, accessories_guide = self._load_style_guide(self.stylist_name)
url = f'{callback_url}/api/style/callback' url = f'{callback_url}/api/style/callback'
@@ -449,7 +463,7 @@ class AsyncStylistAgent:
url url
) )
final_image_path = await self._merge_images(self.outfit_id, user_id, self.stylist_name) final_image_path, _ = await self._merge_images(self.outfit_id, user_id, self.stylist_name)
response_data = self.post_operation( response_data = self.post_operation(
status="stop", status="stop",
message=reason, message=reason,
@@ -465,10 +479,13 @@ class AsyncStylistAgent:
return response_data, total_duration return response_data, total_duration
async def run_quick_batch_styling(self, request_summary, occasions, start_outfit=[], batch_sources=[], user_id="test", callback_url=""): async def run_quick_batch_styling(self, request_summary, occasions, start_outfit: Optional[List] = None, batch_sources: List = [], user_id="test", callback_url=""):
start_time = time.monotonic() start_time = time.monotonic()
# 深拷贝start_outfit 避免实例之间的参数泄漏 确保每个实例都有自己的 start_outfit 副本
self.outfit_items = start_outfit if start_outfit is None:
self.outfit_items = []
else:
self.outfit_items = deepcopy(start_outfit)
stylist_guide, accessories_guide = self._load_style_guide(self.stylist_name) stylist_guide, accessories_guide = self._load_style_guide(self.stylist_name)
url = f'{callback_url}/api/style/callback' url = f'{callback_url}/api/style/callback'
@@ -486,7 +503,7 @@ class AsyncStylistAgent:
url url
) )
final_image_path = await self._merge_images(self.outfit_id, user_id, self.stylist_name) final_image_path, _ = await self._merge_images(self.outfit_id, user_id, self.stylist_name)
response_data = self.post_operation( response_data = self.post_operation(
status="stop", status="stop",
message=reason, message=reason,

View File

@@ -3,7 +3,6 @@ import os
from typing import List, Dict from typing import List, Dict
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from app.server.utils.minio_client import oss_get_image, minio_client from app.server.utils.minio_client import oss_get_image, minio_client
from app.server.utils.minio_config import MINIO_LC_DATA_PATH
from app.config import settings from app.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -79,7 +78,8 @@ def merge_images_to_square(outfit_items: List[Dict[str, str]], max_len=9, add_te
if settings.LOCAL == 1: if settings.LOCAL == 1:
img = Image.open(path).convert('RGB') img = Image.open(path).convert('RGB')
else: else:
img = oss_get_image(oss_client=minio_client, path=f"{MINIO_LC_DATA_PATH}/{path}", data_type="PIL").convert('RGB') img_name = path.rsplit('/', 1)[-1]
img = oss_get_image(oss_client=minio_client, path=f"{settings.MINIO_LC_DATA_PATH}/{img_name}", data_type="PIL").convert('RGB')
# img = Image.open(path).convert('RGB') # img = Image.open(path).convert('RGB')
valid_images.append(img) valid_images.append(img)
except Exception as e: except Exception as e:

View File

@@ -10,9 +10,9 @@ import urllib3
from PIL import Image from PIL import Image
from minio import Minio from minio import Minio
from app.server.utils.minio_config import MINIO_ACCESS, MINIO_SECRET, MINIO_URL, MINIO_SECURE from app.config import settings
minio_client = Minio(MINIO_URL, access_key=MINIO_ACCESS, secret_key=MINIO_SECRET, secure=MINIO_SECURE) minio_client = Minio(settings.MINIO_URL, access_key=settings.MINIO_ACCESS, secret_key=settings.MINIO_SECRET, secure=settings.MINIO_SECURE)
# 自定义 Retry 类 # 自定义 Retry 类

View File

@@ -1,6 +0,0 @@
# minio 配置
MINIO_URL = "www.minio-api.aida.com.hk"
MINIO_ACCESS = 'vXKFLSJkYeEq2DrSZvkB'
MINIO_SECRET = 'uKTZT3x7C43WvPN9QTc99DiRkwddWZrG9Uh3JVlR'
MINIO_SECURE = True
MINIO_LC_DATA_PATH = "lanecarford/lc_image_data"

View File

@@ -5,12 +5,13 @@ services:
dockerfile: Dockerfile dockerfile: Dockerfile
working_dir: /app working_dir: /app
environment: environment:
GOOGLE_APPLICATION_CREDENTIALS: /app/app/request.json GOOGLE_APPLICATION_CREDENTIALS: /google_application_credentials.json
DEBUG: 0 DEBUG: 0
volumes: volumes:
- ./app:/app/app - ./app:/app/app
- ./.env:/app/.env - ./.prod_env:/app/.env
- ./db:/db - ./data:/data
- ./google_application_credentials.json:/google_application_credentials.json
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
ports: ports:
- "10070:8000" - "10070:8000"

0
docs/Edi.docx Normal file → Executable file
View File

0
docs/LC Recommendation Workflow.pdf Normal file → Executable file
View File

0
docs/LC Stylist Rules 总结.docx Normal file → Executable file
View File

0
docs/vera.docx Normal file → Executable file
View File