将生成图片或图片路径写入上下文中 放弃自控图片上下文
This commit is contained in:
@@ -158,8 +158,12 @@ async def chat_stream(request: DeepAgentChatRequest):
|
|||||||
"current_image": ""
|
"current_image": ""
|
||||||
}
|
}
|
||||||
# 用户上传图片
|
# 用户上传图片
|
||||||
|
input_image_content = ''
|
||||||
if request.input_image_paths:
|
if request.input_image_paths:
|
||||||
for path in request.input_image_paths:
|
input_image_content += "\n【附件上传图片路径】\n"
|
||||||
|
for i, path in enumerate(request.input_image_paths):
|
||||||
|
input_image_content += f"- 上传图片{i}: {path}\n"
|
||||||
|
|
||||||
bucket, object_name = path.split('/', 1)
|
bucket, object_name = path.split('/', 1)
|
||||||
image_url = get_presigned_url(oss_client=minio_client, bucket=bucket, object_name=object_name)
|
image_url = get_presigned_url(oss_client=minio_client, bucket=bucket, object_name=object_name)
|
||||||
content.append({"type": "image_url", "image_url": {"url": image_url}})
|
content.append({"type": "image_url", "image_url": {"url": image_url}})
|
||||||
@@ -167,11 +171,16 @@ async def chat_stream(request: DeepAgentChatRequest):
|
|||||||
|
|
||||||
# 用户引用图片
|
# 用户引用图片
|
||||||
if request.quote_image_path:
|
if request.quote_image_path:
|
||||||
|
input_image_content += "\n【附件引用图片路径】\n"
|
||||||
|
input_image_content += f"- 引用图片: {request.quote_image_path}\n"
|
||||||
|
|
||||||
bucket, object_name = request.quote_image_path.split('/', 1)
|
bucket, object_name = request.quote_image_path.split('/', 1)
|
||||||
image_url = get_presigned_url(oss_client=minio_client, bucket=bucket, object_name=object_name)
|
image_url = get_presigned_url(oss_client=minio_client, bucket=bucket, object_name=object_name)
|
||||||
content.append({"type": "image_url", "image_url": {"url": image_url}})
|
content.append({"type": "image_url", "image_url": {"url": image_url}})
|
||||||
files["quote_image"] = request.quote_image_path
|
files["quote_image"] = request.quote_image_path
|
||||||
|
|
||||||
|
if initial_messages:
|
||||||
|
content[0]['text'] += input_image_content
|
||||||
final_messages = {
|
final_messages = {
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
@@ -181,6 +190,7 @@ async def chat_stream(request: DeepAgentChatRequest):
|
|||||||
],
|
],
|
||||||
"files": files
|
"files": files
|
||||||
}
|
}
|
||||||
|
logger.info(final_messages)
|
||||||
async for stream in main_agent.astream(
|
async for stream in main_agent.astream(
|
||||||
final_messages,
|
final_messages,
|
||||||
config=current_config,
|
config=current_config,
|
||||||
@@ -233,8 +243,6 @@ async def chat_stream(request: DeepAgentChatRequest):
|
|||||||
tool_content_blocks = tools_token.content_blocks[0]
|
tool_content_blocks = tools_token.content_blocks[0]
|
||||||
tool_name = tools_token.name
|
tool_name = tools_token.name
|
||||||
logger.info(f"[updates] {tool_name} -- {tool_content_blocks}")
|
logger.info(f"[updates] {tool_name} -- {tool_content_blocks}")
|
||||||
else:
|
|
||||||
logger.info(f"[updates] -- {chunks}")
|
|
||||||
|
|
||||||
elif mode == "messages":
|
elif mode == "messages":
|
||||||
# logger.info(f"[messages] -- {chunks}")
|
# logger.info(f"[messages] -- {chunks}")
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
from deepagents import create_deep_agent
|
from deepagents import create_deep_agent
|
||||||
from deepagents.backends import FilesystemBackend
|
from deepagents.backends import FilesystemBackend
|
||||||
from langchain.agents.middleware import SummarizationMiddleware
|
from langchain.agents.middleware import SummarizationMiddleware, ToolRetryMiddleware
|
||||||
from langgraph.checkpoint.mongodb import MongoDBSaver
|
from langgraph.checkpoint.mongodb import MongoDBSaver
|
||||||
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
||||||
from langgraph.store.memory import InMemoryStore
|
from langgraph.store.memory import InMemoryStore
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
from src.core.config import MONGO_URI
|
from src.core.config import MONGO_URI
|
||||||
from src.server.deep_agent.agents.painter import build_painter_subagent
|
# from src.server.deep_agent.agents.painter import build_painter_subagent
|
||||||
from src.server.deep_agent.agents.researcher import build_researcher_subagent
|
from src.server.deep_agent.agents.researcher import build_researcher_subagent
|
||||||
from src.server.deep_agent.agents.user_profile import user_profile_subagent
|
from src.server.deep_agent.agents.user_profile import user_profile_subagent
|
||||||
from src.server.deep_agent.init_llm import build_main_llm
|
from src.server.deep_agent.init_llm import build_main_llm
|
||||||
from src.server.deep_agent.init_prompt import build_system_prompt
|
from src.server.deep_agent.init_prompt import build_system_prompt
|
||||||
|
from src.server.deep_agent.tools.generate_furniture_sketch import edit_furniture, generate_furniture, edit_quote_upload_furniture
|
||||||
|
|
||||||
client = MongoClient(MONGO_URI)
|
client = MongoClient(MONGO_URI)
|
||||||
checkpointer = MongoDBSaver(
|
checkpointer = MongoDBSaver(
|
||||||
@@ -42,9 +43,9 @@ class CanvasMiddleware:
|
|||||||
|
|
||||||
def build_main_agent(use_report, workspace_dir, enable_thinking):
|
def build_main_agent(use_report, workspace_dir, enable_thinking):
|
||||||
research_subagent = build_researcher_subagent(workspace_dir)
|
research_subagent = build_researcher_subagent(workspace_dir)
|
||||||
painter_subagent = build_painter_subagent(workspace_dir)
|
# painter_subagent = build_painter_subagent(workspace_dir)
|
||||||
subagents = [
|
subagents = [
|
||||||
painter_subagent,
|
# painter_subagent,
|
||||||
research_subagent,
|
research_subagent,
|
||||||
user_profile_subagent
|
user_profile_subagent
|
||||||
]
|
]
|
||||||
@@ -54,6 +55,7 @@ def build_main_agent(use_report, workspace_dir, enable_thinking):
|
|||||||
store=InMemoryStore(),
|
store=InMemoryStore(),
|
||||||
subagents=subagents,
|
subagents=subagents,
|
||||||
checkpointer=checkpointer,
|
checkpointer=checkpointer,
|
||||||
|
tools=[edit_furniture, generate_furniture, edit_quote_upload_furniture],
|
||||||
backend=FilesystemBackend(
|
backend=FilesystemBackend(
|
||||||
root_dir=workspace_dir,
|
root_dir=workspace_dir,
|
||||||
virtual_mode=True, # 重要:關掉虛擬模式 → 真的寫硬碟
|
virtual_mode=True, # 重要:關掉虛擬模式 → 真的寫硬碟
|
||||||
@@ -64,6 +66,11 @@ def build_main_agent(use_report, workspace_dir, enable_thinking):
|
|||||||
trigger=("tokens", 3000),
|
trigger=("tokens", 3000),
|
||||||
keep=("messages", 100),
|
keep=("messages", 100),
|
||||||
),
|
),
|
||||||
|
ToolRetryMiddleware(
|
||||||
|
max_retries=3,
|
||||||
|
backoff_factor=2.0,
|
||||||
|
initial_delay=1.0,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
return main_agent
|
return main_agent
|
||||||
|
|||||||
@@ -1,3 +1,82 @@
|
|||||||
|
# system_prompt = f"""
|
||||||
|
# 你是主调度 Agent(Supervisor),负责理解用户意图并选择合适的子Agent。
|
||||||
|
# 当前参数:
|
||||||
|
# use_report = {use_report}
|
||||||
|
# 系统中存在两个相关子Agent:
|
||||||
|
# 1. user_profile_subagent
|
||||||
|
# 负责收集和维护用户画像信息,包括但不限于:
|
||||||
|
# - style(风格)
|
||||||
|
# - room_type(房间类型)
|
||||||
|
# - budget(预算)
|
||||||
|
# - 其他报告生成所需信息
|
||||||
|
#
|
||||||
|
# 2. research-subagent
|
||||||
|
# 负责生成完整报告、调研、总结、分析。
|
||||||
|
#
|
||||||
|
# 3. painter_subagent
|
||||||
|
# 专门负责家具 sketch 图像的生成与编辑。
|
||||||
|
# - 它内部会使用 generate_furniture(支持 num_images,最多4张)和 edit_furniture 工具。
|
||||||
|
# - 生成图片后会自动更新到对话上下文中。
|
||||||
|
#
|
||||||
|
# ========================
|
||||||
|
# 核心执行规则(严格遵守)
|
||||||
|
# ========================
|
||||||
|
#
|
||||||
|
# 【1】图像生成任务处理(最重要规则)
|
||||||
|
# 当用户请求生成家具图片时(包含“生成”“画”“创建”“给我”“设计”等词):
|
||||||
|
# - 只能**一次性**调用 painter_subagent **一次**。
|
||||||
|
# - 必须在调用时明确告诉它生成的数量。
|
||||||
|
# - **无论用户要求多少张,painter_subagent 最多只能生成 4 张**。
|
||||||
|
# - 不要多次调用 painter_subagent 来凑数量。
|
||||||
|
# - 示例:
|
||||||
|
# - 用户说“生成10张” → 你应该调用 painter_subagent 并指示“生成4张”(因为上限是4),然后直接结束,不再继续调用。
|
||||||
|
# - 用户说“生成3张不同风格的椅子” → 调用一次 painter_subagent 并指示生成3张。
|
||||||
|
#
|
||||||
|
# 【2】调用 painter_subagent 的正确方式
|
||||||
|
# 在给 painter_subagent 的指令中必须包含:
|
||||||
|
# - 用户想要生成的数量(但提醒它上限为4张)。
|
||||||
|
# - 详细的生成需求(风格、类型、材质等)。
|
||||||
|
# - 明确说:“最多只能生成4张,请根据 num_images 参数处理。”
|
||||||
|
#
|
||||||
|
# 禁止行为:
|
||||||
|
# - ❌ 不要连续多次调用 painter_subagent 来生成更多图片。
|
||||||
|
# - ❌ 不要把一次生成任务拆成多次调用。
|
||||||
|
# - ❌ 用户要求10张时,不要生成4张后再问“还要继续生成吗”而是直接限制在4张并回复。
|
||||||
|
#
|
||||||
|
# 【3】当用户请求报告 / 调研 / 分析 / 总结时:
|
||||||
|
# 先判断是否已经具备足够的用户画像信息。
|
||||||
|
# 如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等):
|
||||||
|
# → 调用 user_profile_subagent 收集信息
|
||||||
|
# 不要直接生成报告。
|
||||||
|
# 如果用户画像信息已经完整:
|
||||||
|
# → 调用 research-subagent 生成报告。
|
||||||
|
# ------------------------
|
||||||
|
# 【4】当 use_report = False 时:
|
||||||
|
# - 严禁调用 research-subagent
|
||||||
|
# - 如果用户明确请求报告、调研、总结、分析:
|
||||||
|
#
|
||||||
|
# 请礼貌回复:
|
||||||
|
# "报告功能当前未开启,你可以打开 use_report=True 后我来帮你生成报告。"
|
||||||
|
# - 其他普通问题可以正常回答或调用其他子Agent。
|
||||||
|
# ------------------------
|
||||||
|
# 【5】用户画像优先级规则
|
||||||
|
# 只要用户输入包含以下情况:
|
||||||
|
# - 表达设计需求
|
||||||
|
# - 提供偏好信息(例如风格、预算、房间类型)
|
||||||
|
# - 修改之前的偏好
|
||||||
|
# - 补充报告信息
|
||||||
|
# 都应该优先调用:
|
||||||
|
# user_profile_subagent
|
||||||
|
# 用于更新或收集用户画像。
|
||||||
|
# ------------------------
|
||||||
|
# 【6】调度原则
|
||||||
|
# - user_profile_subagent 只负责 **信息收集**
|
||||||
|
# - research-subagent 只负责 **报告生成**
|
||||||
|
# 不要混用职责。
|
||||||
|
# ========================
|
||||||
|
# """
|
||||||
|
|
||||||
|
|
||||||
def build_system_prompt(use_report):
|
def build_system_prompt(use_report):
|
||||||
system_prompt = f"""
|
system_prompt = f"""
|
||||||
你是主调度 Agent(Supervisor),负责理解用户意图并选择合适的子Agent。
|
你是主调度 Agent(Supervisor),负责理解用户意图并选择合适的子Agent。
|
||||||
@@ -14,15 +93,37 @@ def build_system_prompt(use_report):
|
|||||||
2. research-subagent
|
2. research-subagent
|
||||||
负责生成完整报告、调研、总结、分析。
|
负责生成完整报告、调研、总结、分析。
|
||||||
|
|
||||||
3. painter_subagent
|
========================
|
||||||
负责根据用户描述,构造适用于 生成家具sketch的prompt或编辑家具sketch的prompt
|
核心执行规则(必须严格遵守)
|
||||||
1.利用prompt用工具生成图片.
|
========================
|
||||||
2.利用prompt和图片路径用工具编辑图片.
|
|
||||||
|
【1】图像生成与编辑任务处理(最重要规则)
|
||||||
|
当用户请求生成或修改家具图片时(包含“生成”“画”“创建”“设计”“修改”“帮我改”等词):
|
||||||
|
|
||||||
|
- 只能**一次性**调用图片相关工具(edit_quote_upload_furniture、edit_furniture、generate_furniture 等)。
|
||||||
|
- 无论用户要求多少张,生成类工具最多只能生成 4 张。
|
||||||
|
- 如果用户消息中出现有效的 MinIO 图片路径,或明确提到“上传的图片”“我提供的图片”“这张图”等 → 优先使用 `edit_quote_upload_furniture`。
|
||||||
|
- 如果是本对话中刚刚生成的图片 → 使用 `edit_furniture`。
|
||||||
|
|
||||||
|
**重要输出规则**:
|
||||||
|
- 你**绝对不能**在回复中输出任何文件路径、MinIO 路径、图片 URL 或类似 "uploads/..."、"furniture/sketches/..." 的内容。
|
||||||
|
- 所有图片都会通过系统其他方式展示给用户,你不需要也不允许展示路径。
|
||||||
|
- 你的最终回复只需要关注**工具是否成功调用**。
|
||||||
|
- 如果工具调用成功:可以回复类似“已为你生成/修改图片,请查看”或直接不回复(让系统展示图片)。
|
||||||
|
- 如果工具调用失败或返回错误信息:可以礼貌告知用户“图片生成/修改失败,请稍后重试”或具体描述错误原因(但仍不要包含任何路径)。
|
||||||
|
|
||||||
|
【2】调用图片工具的正确方式
|
||||||
|
- 必须一次性调用工具,不要拆分成多次调用。
|
||||||
|
- 在给图片工具的指令中,明确说明生成或修改的数量(但上限为4)。
|
||||||
|
- 示例:用户说“生成10张” → 只调用一次工具并限制为4张,然后正常回复。
|
||||||
|
|
||||||
|
**禁止行为**:
|
||||||
|
- ❌ 不要在任何回复中输出图片路径或文件路径。
|
||||||
|
- ❌ 不要多次调用生成工具来凑数量。
|
||||||
|
- ❌ 不要把路径告诉用户。
|
||||||
|
- ❌ 工具成功后不要描述“生成了哪些路径的图片”。
|
||||||
|
|
||||||
========================
|
【3】当用户请求报告 / 调研 / 分析 / 总结时:
|
||||||
执行规则
|
|
||||||
========================
|
|
||||||
【1】当用户请求报告 / 调研 / 分析 / 总结时:
|
|
||||||
先判断是否已经具备足够的用户画像信息。
|
先判断是否已经具备足够的用户画像信息。
|
||||||
如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等):
|
如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等):
|
||||||
→ 调用 user_profile_subagent 收集信息
|
→ 调用 user_profile_subagent 收集信息
|
||||||
@@ -30,7 +131,7 @@ def build_system_prompt(use_report):
|
|||||||
如果用户画像信息已经完整:
|
如果用户画像信息已经完整:
|
||||||
→ 调用 research-subagent 生成报告。
|
→ 调用 research-subagent 生成报告。
|
||||||
------------------------
|
------------------------
|
||||||
【2】当 use_report = False 时:
|
【4】当 use_report = False 时:
|
||||||
- 严禁调用 research-subagent
|
- 严禁调用 research-subagent
|
||||||
- 如果用户明确请求报告、调研、总结、分析:
|
- 如果用户明确请求报告、调研、总结、分析:
|
||||||
|
|
||||||
@@ -38,7 +139,7 @@ def build_system_prompt(use_report):
|
|||||||
"报告功能当前未开启,你可以打开 use_report=True 后我来帮你生成报告。"
|
"报告功能当前未开启,你可以打开 use_report=True 后我来帮你生成报告。"
|
||||||
- 其他普通问题可以正常回答或调用其他子Agent。
|
- 其他普通问题可以正常回答或调用其他子Agent。
|
||||||
------------------------
|
------------------------
|
||||||
【3】用户画像优先级规则
|
【5】用户画像优先级规则
|
||||||
只要用户输入包含以下情况:
|
只要用户输入包含以下情况:
|
||||||
- 表达设计需求
|
- 表达设计需求
|
||||||
- 提供偏好信息(例如风格、预算、房间类型)
|
- 提供偏好信息(例如风格、预算、房间类型)
|
||||||
@@ -48,11 +149,17 @@ def build_system_prompt(use_report):
|
|||||||
user_profile_subagent
|
user_profile_subagent
|
||||||
用于更新或收集用户画像。
|
用于更新或收集用户画像。
|
||||||
------------------------
|
------------------------
|
||||||
【4】调度原则
|
【6】调度原则
|
||||||
- user_profile_subagent 只负责 **信息收集**
|
- user_profile_subagent 只负责 **信息收集**
|
||||||
- research-subagent 只负责 **报告生成**
|
- research-subagent 只负责 **报告生成**
|
||||||
不要混用职责。
|
不要混用职责。
|
||||||
========================
|
========================
|
||||||
|
重要提醒(最高优先级):
|
||||||
|
在整个对话过程中,你**绝对禁止**输出任何包含以下内容的文字:
|
||||||
|
- 以 "uploads/"、"furniture/"、"projects/"、"sketches/" 开头的路径
|
||||||
|
- 任何 .png、.jpg 结尾的路径
|
||||||
|
- 任何 http 开头的图片链接(除非系统明确要求)
|
||||||
|
所有图片展示均由系统统一处理,你只需负责正确调用工具。
|
||||||
"""
|
"""
|
||||||
return system_prompt
|
return system_prompt
|
||||||
|
|
||||||
@@ -71,6 +178,7 @@ def build_painter_prompt():
|
|||||||
- 或任何“基于多张图片做合并提取”的表达
|
- 或任何“基于多张图片做合并提取”的表达
|
||||||
👉 必须使用:
|
👉 必须使用:
|
||||||
edit_furniture
|
edit_furniture
|
||||||
|
|
||||||
👉 严格要求:
|
👉 严格要求:
|
||||||
- 不允许调用 generate_furniture
|
- 不允许调用 generate_furniture
|
||||||
- 不允许重新生成整张图
|
- 不允许重新生成整张图
|
||||||
@@ -85,43 +193,53 @@ def build_painter_prompt():
|
|||||||
如果用户输入不明确(例如:“改成绿色”):
|
如果用户输入不明确(例如:“改成绿色”):
|
||||||
👉 一律视为【编辑类】
|
👉 一律视为【编辑类】
|
||||||
👉 使用 edit_furniture
|
👉 使用 edit_furniture
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
【二、关于图片来源(关键规则)】
|
【二、generate_furniture 参数规则(重要)】
|
||||||
- 当前系统已经提供了一张“当前图片”(不需要你生成 image_url)
|
当需要生成多张图片时:
|
||||||
- ❗禁止你自行编造 image_url
|
- prompt 必须始终描述 **单张家具**(single furniture piece),不要在 prompt 里写入 "Generate 4 different..."、"multiple chairs"、“4 variations”等数量相关的词。
|
||||||
- ❗禁止你猜测 image_url
|
- 正确的 prompt 风格示例(单张):
|
||||||
- edit_furniture 会自动从上下文获取图片
|
"A modern minimalist dining chair made of light oak wood and white leather, with slim metal legs, clean lines, elegant proportions, photographed in a bright Scandinavian living room with natural sunlight, high detail, 8k resolution, professional furniture photography, neutral background."
|
||||||
|
|
||||||
|
- 如何处理不同风格:
|
||||||
|
- 如果用户想要多种风格(modern, vintage, industrial, minimalist 等),你应该**多次调用 generate_furniture 工具**(每次调用使用不同风格的 prompt,num_images=1)。
|
||||||
|
- 但由于系统限制单次用户请求最多生成4张图片:
|
||||||
|
- 当用户要求生成超过4张或很多变体时,你最多只调用工具4次(或设置 num_images=4,但 prompt 保持 single)。
|
||||||
|
- 优先使用 num_images=4 + 一个高质量的 single prompt,让模型自动生成4个轻微不同的变体。
|
||||||
|
- 如果用户明确要“明显不同风格”,则分多次调用(但总数量不超过4张)。
|
||||||
|
|
||||||
|
- num_images 参数:
|
||||||
|
- 默认 1
|
||||||
|
- 最大只能设置为 4
|
||||||
|
- 当用户要求10张、8张等时 → 自动限制为 num_images=4,并说明“由于系统限制,最多生成4张”
|
||||||
|
|
||||||
|
正确调用示例(推荐):
|
||||||
|
- 用户想要4张不同风格 → 使用 num_images=4 + 一个清晰的 single chair prompt(让模型自然变体),或分4次调用每次1张不同风格。
|
||||||
|
- 永远不要把“4 different designs” “generate 4 chairs”这类词写进 prompt 文本中。
|
||||||
--------------------------------
|
--------------------------------
|
||||||
【三、参数构造规则】
|
【三、edit_furniture 参数规则】
|
||||||
调用 edit_furniture 时:
|
- 只需提供 prompt 参数,格式为详细的英文编辑指令。
|
||||||
- 只需要提供:
|
- prompt 示例:
|
||||||
{
|
"Change the sofa color to deep green while keeping the original modern minimalist style and structure."
|
||||||
"prompt": "<英文图像编辑描述>"
|
- edit_furniture 会自动使用当前上下文中的最新图片,无需你提供 image_url。
|
||||||
}
|
|
||||||
- prompt 要求:
|
|
||||||
- 清晰描述修改内容
|
|
||||||
- 保留原结构(除非用户明确要求改变)
|
|
||||||
- 示例:
|
|
||||||
"Change the sofa to green color while keeping the original lines and structure."
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
【四、禁止行为(强约束)】
|
【四、禁止行为(严格禁止)】
|
||||||
你绝对不能:
|
- ❌ 在编辑意图时调用 generate_furniture
|
||||||
- ❌ 在编辑场景调用 generate_furniture
|
- ❌ 在生成意图时调用 edit_furniture
|
||||||
- ❌ 编造 image_url
|
- ❌ 自行编造 image_url
|
||||||
- ❌ 忽略“修改类”意图
|
- ❌ 输出任何工具调用细节、URL、路径给用户
|
||||||
- ❌ 因为信息少就拒绝调用工具
|
- ❌ 拒绝调用工具(除非工具本身不可用)
|
||||||
--------------------------------
|
--------------------------------
|
||||||
【五、用户回复规则(必须遵守)】
|
【五、用户回复规则(必须遵守)】
|
||||||
你对用户的最终回复只能是以下格式之一:
|
- 生成成功时:
|
||||||
- "图片已成功生成!"
|
- "已为你生成 {num} 张家具设计图!"
|
||||||
- "已按你的要求完成修改,图片已更新!"
|
- "图片已成功生成,请查看效果。"
|
||||||
❗禁止输出:
|
|
||||||
- 路径
|
- 编辑成功时:
|
||||||
- URL
|
- "已按你的要求完成修改,图片已更新!"
|
||||||
- 工具参数
|
- "修改完成,新的版本已生成。"
|
||||||
- 解释过程
|
请根据实际生成/编辑的数量自然调整回复,不要生硬照抄。
|
||||||
--------------------------------
|
现在开始工作,请根据用户下一条输入严格遵循以上规则进行工具调用。
|
||||||
现在开始工作。
|
|
||||||
"""
|
"""
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from langchain_core.runnables import RunnableConfig
|
from langchain_core.runnables import RunnableConfig
|
||||||
from minio import Minio
|
from minio import Minio
|
||||||
from pathlib import Path
|
# from pathlib import Path
|
||||||
from datetime import datetime
|
# from datetime import datetime
|
||||||
from langchain_core.tools import tool
|
from langchain_core.tools import tool
|
||||||
from langgraph.prebuilt import ToolRuntime
|
from langgraph.prebuilt import ToolRuntime
|
||||||
from src.core.config import settings, MONGO_URI
|
from src.core.config import settings, MONGO_URI
|
||||||
from src.server.deep_agent.utils.mongodb_util import ThreadImageMinIOStore
|
# from src.server.deep_agent.utils.mongodb_util import ThreadImageMinIOStore
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
minio_client = Minio(settings.MINIO_URL, access_key=settings.MINIO_ACCESS, secret_key=settings.MINIO_SECRET, secure=settings.MINIO_SECURE)
|
minio_client = Minio(settings.MINIO_URL, access_key=settings.MINIO_ACCESS, secret_key=settings.MINIO_SECRET, secure=settings.MINIO_SECURE)
|
||||||
image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
|
# image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
async def generate_furniture(prompt: str, runtime: ToolRuntime):
|
async def generate_furniture(runtime: ToolRuntime, prompts: list[str] = None, num_images: Optional[int] = 1):
|
||||||
"""
|
"""
|
||||||
使用图像生成模型根据用户提供的详细英文提示词,从零生成一张全新的家具设计草图。
|
使用图像生成模型根据用户提供的详细英文提示词,从零生成一张全新的家具设计草图。
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ async def generate_furniture(prompt: str, runtime: ToolRuntime):
|
|||||||
参数说明:
|
参数说明:
|
||||||
- prompt (str): **必须是详细的英文提示词**,越详细越好,包含家具类型、风格、颜色、材质、尺寸比例、背景、视角、光影等具体要求。
|
- prompt (str): **必须是详细的英文提示词**,越详细越好,包含家具类型、风格、颜色、材质、尺寸比例、背景、视角、光影等具体要求。
|
||||||
示例:"Generate a modern minimalist dining chair made of light oak wood and white leather, with slim metal legs, photographed in a bright Scandinavian living room with natural sunlight, high detail, 8k resolution."
|
示例:"Generate a modern minimalist dining chair made of light oak wood and white leather, with slim metal legs, photographed in a bright Scandinavian living room with natural sunlight, high detail, 8k resolution."
|
||||||
|
- num_images (int, 可选): 要生成的图片数量,默认 1 张。最大只能是 4 张。如果输入超过 4,会自动限制为 4。
|
||||||
|
|
||||||
返回值:
|
返回值:
|
||||||
返回新生成家具图片的 image_url,后续对话中 Agent 可直接引用该图片进行描述、进一步编辑或分析。
|
返回新生成家具图片的 image_url,后续对话中 Agent 可直接引用该图片进行描述、进一步编辑或分析。
|
||||||
@@ -42,85 +45,232 @@ async def generate_furniture(prompt: str, runtime: ToolRuntime):
|
|||||||
- 生成的图片会自动携带到整个对话上下文中,支持后续使用 edit_furniture 等工具进行迭代修改。
|
- 生成的图片会自动携带到整个对话上下文中,支持后续使用 edit_furniture 等工具进行迭代修改。
|
||||||
- 如果需要生成多个方案,可以多次调用本工具或在 prompt 中明确要求生成不同变体。
|
- 如果需要生成多个方案,可以多次调用本工具或在 prompt 中明确要求生成不同变体。
|
||||||
"""
|
"""
|
||||||
current_checkpoint_id = runtime.store.get(namespace=("image_history",), key="checkpoint_id", ).value.get("current_checkpoint_id")
|
if num_images is None or num_images < 1:
|
||||||
|
num_images = 1
|
||||||
|
elif num_images > 4:
|
||||||
|
num_images = 4
|
||||||
|
# current_checkpoint_id = runtime.store.get(namespace=("image_history",), key="checkpoint_id", ).value.get("current_checkpoint_id")
|
||||||
|
|
||||||
logger.info(f"\n[系统日志] 正在调用 generate_furniture ...当前checkpoint_id={current_checkpoint_id}")
|
logger.info(f"\n[系统日志] 正在调用 generate_furniture ...当前checkpoint_id={current_checkpoint_id}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_url = await generate_or_edit_image(prompt=prompt)
|
bucket_name = "fida-public-bucket"
|
||||||
|
object_name = f"furniture/sketches/{uuid.uuid4()}"
|
||||||
|
image_urls = []
|
||||||
|
for i in range(num_images):
|
||||||
|
image_urls.append(await generate_or_edit_image(prompt=prompts[i], bucket_name=bucket_name, object_name=f"{object_name}-{i}.png"))
|
||||||
|
|
||||||
if image_url:
|
# if image_urls:
|
||||||
image_store.save_image_path(thread_id=current_checkpoint_id, object_path=image_url, metadata={"prompt": prompt, "generated_at": str(datetime.now())})
|
# image_store.save_image_path(thread_id=current_checkpoint_id, object_path=image_urls, metadata={"prompt": prompt, "generated_at": str(datetime.now())})
|
||||||
return image_url
|
return image_urls
|
||||||
else:
|
# else:
|
||||||
return "Image generation failed."
|
# return "Image generation failed."
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"绘图流程异常:{e}")
|
logger.warning(f"绘图流程异常:{e}")
|
||||||
return "generate furniture error"
|
return "generate furniture error"
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
async def edit_furniture(prompt: str, runtime: ToolRuntime, config: RunnableConfig):
|
async def edit_furniture(runtime: ToolRuntime, config: RunnableConfig, input_image_paths: list[str] = None, prompts: list[str] = None, ):
|
||||||
"""
|
"""
|
||||||
使用先进的图像编辑模型(image editing model)对家具设计草图进行精准修改。
|
使用先进的图像编辑模型对家具设计草图进行精准修改。
|
||||||
|
|
||||||
功能说明:
|
功能说明:
|
||||||
- 根据用户提供的**详细英文提示词**,生成修改后的新家具图片。
|
- 支持批量处理多张家具图片,根据对应的提示词生成修改后的新图片。
|
||||||
|
- input_image_paths 和 prompts 必须一一对应,数量完全一致。
|
||||||
|
- 最多支持同时处理 4 对图片和提示词(即最多 4 张图片)。
|
||||||
|
|
||||||
参数说明:
|
参数说明:
|
||||||
- prompt (str): **必须是详细的英文提示词**,描述想要的具体修改(风格、颜色、材质、形状、添加/删除元素、比例等)。
|
|
||||||
示例:"Change the sofa to a modern minimalist style with dark gray fabric and metal legs, add a matching coffee table, make the overall lighting warmer and more luxurious."
|
- input_image_paths (list[str]):
|
||||||
|
输入图片在 MinIO 中的存储路径列表。
|
||||||
|
示例:["furniture/designs/sofa_concept_v1.png", "projects/room_2026/chair_v2.jpg"]
|
||||||
|
注意:路径必须是有效的 MinIO 对象路径,工具会自动下载对应图片。
|
||||||
|
|
||||||
|
- prompts (list[str]):
|
||||||
|
与图片一一对应的详细英文提示词列表。
|
||||||
|
每个提示词描述对对应图片的具体修改要求(风格、颜色、材质、形状、添加/删除元素等)。
|
||||||
|
示例:["Change the sofa to a modern minimalist style with dark gray fabric and metal legs, add a matching coffee table.",
|
||||||
|
"Convert the chair to Scandinavian Nordic style with light wood and soft beige upholstery."]
|
||||||
|
|
||||||
|
使用要求(重要):
|
||||||
|
- input_image_paths 和 prompts 的长度必须完全相同。
|
||||||
|
- 列表长度必须在 1 到 4 之间(最多 4 对)。
|
||||||
|
- input_image_paths[0] 对应 prompts[0],以此类推,一一对应进行编辑。
|
||||||
|
|
||||||
使用场景:
|
使用场景:
|
||||||
- 家具设计迭代
|
- 家具设计方案迭代
|
||||||
- 室内设计方案修改
|
- 室内设计多方案对比修改
|
||||||
- 风格转换(现代/北欧/工业风等)
|
- 批量风格转换(现代/北欧/工业/奢华风等)
|
||||||
- 材质/颜色调整
|
- 材质、颜色、细节批量调整
|
||||||
|
|
||||||
|
示例调用:
|
||||||
|
input_image_paths = ["designs/sofa1.png", "designs/chair1.png"]
|
||||||
|
prompts = [
|
||||||
|
"Make the sofa more luxurious with velvet fabric and gold accents.",
|
||||||
|
"Change the chair to a sleek modern design with black leather and chrome legs."
|
||||||
|
]
|
||||||
"""
|
"""
|
||||||
image_history = runtime.store.get(namespace=("image_history",), key="checkpoint_id", )
|
|
||||||
last_checkpoint_id = image_history.value.get("last_checkpoint_id")
|
|
||||||
current_checkpoint_id = image_history.value.get("current_checkpoint_id")
|
|
||||||
|
|
||||||
logger.info(f"\n[系统日志] 正在调用 edit_furniture ...current_checkpoint_id={current_checkpoint_id} --- last_checkpoint_id={last_checkpoint_id}")
|
# image_history = runtime.store.get(namespace=("image_history",), key="checkpoint_id", )
|
||||||
|
# last_checkpoint_id = image_history.value.get("last_checkpoint_id")
|
||||||
input_path = []
|
# current_checkpoint_id = image_history.value.get("current_checkpoint_id")
|
||||||
if image_store.get_image_path(last_checkpoint_id):
|
#
|
||||||
current_image_path = image_store.get_image_path(last_checkpoint_id).get("current_image_path", False)
|
# logger.info(f"\n[系统日志] 正在调用 edit_furniture ...current_checkpoint_id={current_checkpoint_id} --- last_checkpoint_id={last_checkpoint_id}")
|
||||||
else:
|
#
|
||||||
current_image_path = None
|
# if image_store.get_image_path(last_checkpoint_id):
|
||||||
|
# current_image_path = image_store.get_image_path(last_checkpoint_id).get("current_image_path", False)
|
||||||
|
# if current_image_path:
|
||||||
|
# if isinstance(current_image_path, list):
|
||||||
|
# # 只取最后一张
|
||||||
|
# current_image_path = current_image_path[-1]
|
||||||
|
# else:
|
||||||
|
# current_image_path = None
|
||||||
|
|
||||||
|
# input_path = []
|
||||||
try:
|
try:
|
||||||
user_input_image_paths = runtime.state.get("files").get("input_image", [])
|
# user_input_image_paths = runtime.state.get("files").get("input_image", [])
|
||||||
user_quote_image_path = runtime.state.get("files").get("quote_image", "")
|
# user_quote_image_path = runtime.state.get("files").get("quote_image", "")
|
||||||
|
result = []
|
||||||
if current_image_path:
|
if len(input_image_paths):
|
||||||
if len(user_input_image_paths) or current_image_path:
|
for i in range(len(input_image_paths)):
|
||||||
for path in user_input_image_paths:
|
|
||||||
input_path.append(path)
|
|
||||||
if user_quote_image_path:
|
|
||||||
input_path.append(user_quote_image_path)
|
|
||||||
if not len(user_input_image_paths) and not user_quote_image_path:
|
|
||||||
input_path = [current_image_path]
|
|
||||||
|
|
||||||
bucket_name = "fida-public-bucket"
|
bucket_name = "fida-public-bucket"
|
||||||
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
|
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
|
||||||
image_url = await generate_or_edit_image(input_path=input_path, prompt=prompt, bucket_name=bucket_name, object_name=object_name)
|
image_url = await generate_or_edit_image(input_path=[input_image_paths[i]], prompt=prompts[i], bucket_name=bucket_name, object_name=f"{object_name}-{i}.png")
|
||||||
if image_url:
|
result.append(image_url)
|
||||||
image_store.save_image_path(thread_id=current_checkpoint_id, object_path=image_url, metadata={"prompt": prompt, "generated_at": str(datetime.now())})
|
# image_url = await generate_or_edit_image(input_path=input_path, prompt=prompt, bucket_name=bucket_name, object_name=object_name)
|
||||||
return image_url
|
# if image_url:
|
||||||
else:
|
# image_store.save_image_path(thread_id=current_checkpoint_id, object_path=[image_url], metadata={"prompt": prompt, "generated_at": str(datetime.now())})
|
||||||
return "Image generation failed."
|
return result
|
||||||
else:
|
# else:
|
||||||
return "The picture to be edited does not exist."
|
# return "Image generation failed."
|
||||||
else:
|
# else:
|
||||||
return "No recent image found, please upload or cite it"
|
# return "The picture to be edited does not exist."
|
||||||
|
# else:
|
||||||
|
# return "No recent image found, please upload or cite it"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"edit_furniture error :{e}")
|
logger.warning(f"edit_furniture error :{e}")
|
||||||
return "edit_furniture error"
|
return "edit_furniture error"
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
async def edit_quote_upload_furniture(image_paths: list[str] = None, mode: str = "auto", prompts: list[str] = None, ):
|
||||||
|
"""
|
||||||
|
使用先进的图像编辑模型对家具图片进行精准批量修改。
|
||||||
|
|
||||||
|
支持四种模式:
|
||||||
|
- one_to_one(最常用):多张图片 + 多个提示词,一一对应编辑
|
||||||
|
- one_to_many:多张图片 + 1个提示词(所有图片统一修改)
|
||||||
|
- many_to_one:1张图片 + 多个提示词(同一张图生成多个不同变体,例如不同颜色)
|
||||||
|
- many_to_many(新增):多张图片 + 多个提示词,一一对应(多对多交叉编辑)
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- image_paths (list[str]): MinIO 图片路径列表,长度建议 1~4
|
||||||
|
- prompts (list[str]): 详细英文提示词列表
|
||||||
|
- mode (str): "one_to_one", "one_to_many", "many_to_one", "many_to_many", "auto"(默认自动判断)
|
||||||
|
|
||||||
|
使用要求:
|
||||||
|
- image_paths 长度必须在 1~4 之间
|
||||||
|
- mode="auto" 时会根据长度智能判断
|
||||||
|
- many_to_many 模式下:image_paths 和 prompts 的长度必须完全相同
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
示例1:many_to_many(多对多,一一对应)
|
||||||
|
image_paths = ["sofa1.png", "chair1.png", "table1.png"]
|
||||||
|
prompts = [
|
||||||
|
"Change to bright yellow modern style.",
|
||||||
|
"Change to deep green luxury style.",
|
||||||
|
"Change to soft beige Scandinavian style."
|
||||||
|
]
|
||||||
|
mode = "many_to_many"
|
||||||
|
|
||||||
|
示例2:many_to_one(同一张图多个颜色版本)
|
||||||
|
image_paths = ["sofa_original.png"]
|
||||||
|
prompts = ["yellow version", "green version", "blue version", "black version"]
|
||||||
|
mode = "many_to_one"
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# ====================== 参数校验(直接返回错误信息) ======================
|
||||||
|
if not image_paths or len(image_paths) < 1 or len(image_paths) > 4:
|
||||||
|
return f"参数错误:image_paths 必须提供,且长度需要在 1 到 4 张之间。目前收到 {len(image_paths) if image_paths else 0} 张。"
|
||||||
|
|
||||||
|
if not prompts:
|
||||||
|
return "参数错误:prompts 不能为空,请至少提供一个修改提示词。"
|
||||||
|
|
||||||
|
if mode not in ["one_to_one", "one_to_many", "many_to_one", "many_to_many", "auto"]:
|
||||||
|
return f"参数错误:mode 参数无效。可用值:one_to_one, one_to_many, many_to_one, many_to_many, auto。当前收到:{mode}"
|
||||||
|
|
||||||
|
# Auto 模式智能判断
|
||||||
|
if mode == "auto":
|
||||||
|
if len(image_paths) == 1 and len(prompts) > 1:
|
||||||
|
mode = "many_to_one"
|
||||||
|
elif len(prompts) == 1:
|
||||||
|
mode = "one_to_many"
|
||||||
|
elif len(image_paths) == len(prompts):
|
||||||
|
mode = "many_to_many" # 新增:数量相等时优先 many_to_many
|
||||||
|
else:
|
||||||
|
mode = "one_to_one"
|
||||||
|
|
||||||
|
# 各模式严格校验
|
||||||
|
if mode == "many_to_one":
|
||||||
|
if len(image_paths) != 1:
|
||||||
|
return f"参数错误:many_to_one 模式只能传入 1 张图片,当前传入了 {len(image_paths)} 张。"
|
||||||
|
if len(prompts) < 1:
|
||||||
|
return "参数错误:many_to_one 模式下 prompts 至少需要 1 个。"
|
||||||
|
|
||||||
|
elif mode == "one_to_many":
|
||||||
|
if len(prompts) != 1:
|
||||||
|
return f"参数错误:one_to_many 模式下 prompts 必须只有 1 个,当前有 {len(prompts)} 个。"
|
||||||
|
|
||||||
|
elif mode in ["one_to_one", "many_to_many"]:
|
||||||
|
if len(prompts) != len(image_paths):
|
||||||
|
return (f"参数错误:{mode} 模式下 image_paths 和 prompts 数量必须完全一致。\n"
|
||||||
|
f"当前 image_paths 有 {len(image_paths)} 张,prompts 有 {len(prompts)} 个。")
|
||||||
|
|
||||||
|
# ====================== 执行编辑 ======================
|
||||||
|
result = []
|
||||||
|
bucket_name = "fida-public-bucket"
|
||||||
|
|
||||||
|
if mode == "many_to_one":
|
||||||
|
# 同一张图片 + 多个 prompt
|
||||||
|
base_image = image_paths[0]
|
||||||
|
for i, prompt in enumerate(prompts):
|
||||||
|
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
|
||||||
|
image_url = await generate_or_edit_image(
|
||||||
|
input_path=[base_image],
|
||||||
|
prompt=prompt,
|
||||||
|
bucket_name=bucket_name,
|
||||||
|
object_name=f"{object_name}-var{i}.png"
|
||||||
|
)
|
||||||
|
result.append(image_url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# one_to_one、many_to_many、one_to_many 统一处理
|
||||||
|
for i in range(len(image_paths)):
|
||||||
|
# 根据模式决定当前使用的 prompt
|
||||||
|
if mode == "one_to_many":
|
||||||
|
current_prompt = prompts[0]
|
||||||
|
else:
|
||||||
|
current_prompt = prompts[i] # one_to_one 和 many_to_many 都用对应位置的 prompt
|
||||||
|
|
||||||
|
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
|
||||||
|
image_url = await generate_or_edit_image(
|
||||||
|
input_path=[image_paths[i]],
|
||||||
|
prompt=current_prompt,
|
||||||
|
bucket_name=bucket_name,
|
||||||
|
object_name=f"{object_name}-{i}.png"
|
||||||
|
)
|
||||||
|
result.append(image_url)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"edit_quote_upload_furniture 执行异常: {e}", exc_info=True)
|
||||||
|
return f"工具执行失败:{str(e)},请检查参数后重试。"
|
||||||
|
|
||||||
|
|
||||||
async def generate_or_edit_image(input_path=None, bucket_name="fida-public-bucket",
|
async def generate_or_edit_image(input_path=None, bucket_name="fida-public-bucket",
|
||||||
object_name=f"furniture/sketches/{uuid.uuid4()}.png",
|
object_name=f"furniture/sketches/{uuid.uuid4()}.png",
|
||||||
prompt="Generate a modern minimalist dining chair made of light "
|
prompt="Generate a modern minimalist dining chair made of light "
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class ThreadImageMinIOStore:
|
|||||||
def save_image_path(
|
def save_image_path(
|
||||||
self,
|
self,
|
||||||
thread_id: str,
|
thread_id: str,
|
||||||
object_path: str, # MinIO 中的相對路徑,例如 "test/123.png" 或 "images/20250320/abc.png"
|
object_path: list, # MinIO 中的相對路徑,例如 "test/123.png" 或 "images/20250320/abc.png"
|
||||||
metadata: Optional[dict] = None
|
metadata: Optional[dict] = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -137,7 +137,7 @@ if __name__ == '__main__':
|
|||||||
image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
|
image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
|
||||||
success = image_store.save_image_path(
|
success = image_store.save_image_path(
|
||||||
thread_id="121233",
|
thread_id="121233",
|
||||||
object_path="test/123.png",
|
object_path=["test/123.png"],
|
||||||
metadata={"prompt": "prompt", "generated_at": str(datetime.now())})
|
metadata={"prompt": "prompt", "generated_at": str(datetime.now())})
|
||||||
print(success)
|
print(success)
|
||||||
info = image_store.get_image_path("121233")
|
info = image_store.get_image_path("121233")
|
||||||
|
|||||||
@@ -182,10 +182,10 @@ def check_and_extract_minio_image(url: str) -> dict[str, str]:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
url = 'fida-test/furniture/sketches/1b82b2db-8019-4796-b2cc-11fb24c7799d.png'
|
urls = ["fida-public-bucket/furniture/sketches/0193c9b2-d8dd-40fc-b715-3ce0daab7abf.png-0.png", "fida-public-bucket/furniture/sketches/bab54cdf-0a60-4806-8c6b-17b836aec1eb.png-1.png", "fida-public-bucket/furniture/sketches/6c993266-95d2-42ee-826b-933b0e344b81.png-2.png"]
|
||||||
read_type = "2"
|
# read_type = "2"
|
||||||
img = oss_get_image(oss_client=minio_client, bucket=url.split('/')[0], object_name=url[url.find('/') + 1:])
|
for url in urls:
|
||||||
img.show()
|
img = oss_get_image(oss_client=minio_client, bucket=url.split('/')[0], object_name=url[url.find('/') + 1:])
|
||||||
img.save("result.png")
|
img.show()
|
||||||
|
# img.save("result.png")
|
||||||
# get_presigned_url(oss_client=minio_client, bucket="fida-test", object_name="furniture/sketches/07bf4cfe-4502-4821-b78f-7727bf409498.png")
|
# get_presigned_url(oss_client=minio_client, bucket="fida-test", object_name="furniture/sketches/07bf4cfe-4502-4821-b78f-7727bf409498.png")
|
||||||
#
|
|
||||||
|
|||||||
Reference in New Issue
Block a user