重构图像生成和搜索工具;更新主代理来处理输入图像
- 更新了“generate_image.py”以接受输入图像以增强图像生成。 - 修改了`pexels_search.py`和`unsplash_search.py`以将日志记录和上传路径从“explorer”更改为“explore”。 - 调整了“print_graph”和“sketch_graph”以提取最新的用户输入并处理输入图像以生成打印和草图图像。 - 重构“generate_print_tool”和“generate_sketch_tool”以接受输入图像。 - 更新了“main_agent.py”以包含状态中的输入图像并调整了图形构建过程。 - 增强了“service.py”来管理输入图像并改进了流媒体期间的事件处理。 - 更新了新软件包和版本的“pyproject.toml”和“uv.lock”中的依赖项。
This commit is contained in:
@@ -27,7 +27,7 @@ async def fashion_agent_stream(request_item: FashionAgentRequest):
|
|||||||
- **call_design**: 是否直接调用 design 生成设计系列
|
- **call_design**: 是否直接调用 design 生成设计系列
|
||||||
- **design_request_data**: design 请求参数(objects, process_id, requestId, callback_url)
|
- **design_request_data**: design 请求参数(objects, process_id, requestId, callback_url)
|
||||||
- **call_trending**: 是否直接调用 trending 趋势分析
|
- **call_trending**: 是否直接调用 trending 趋势分析
|
||||||
- **call_explor**: 是否直接调用 explorer 灵感探索
|
- **call_explore**: 是否直接调用 explore 灵感探索
|
||||||
- **provider**: 图片源 (pexels/unsplash),默认 unsplash
|
- **provider**: 图片源 (pexels/unsplash),默认 unsplash
|
||||||
|
|
||||||
返回 SSE 事件流:
|
返回 SSE 事件流:
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ from pydantic import BaseModel, Field
|
|||||||
class FashionAgentRequest(BaseModel):
|
class FashionAgentRequest(BaseModel):
|
||||||
"""服装设计 Agent 请求"""
|
"""服装设计 Agent 请求"""
|
||||||
|
|
||||||
|
thread_id: str = Field(description="会话id")
|
||||||
|
|
||||||
message: str = Field(default="", description="用户输入的消息")
|
message: str = Field(default="", description="用户输入的消息")
|
||||||
user_id: str = Field(default="test-agent", description="用户ID,用于生成图片存储路径")
|
user_id: str = Field(default="test-agent", description="用户ID,用于生成图片存储路径")
|
||||||
|
input_images: list[str] = Field(default=[], description="用户上传图片地址")
|
||||||
enable_thinking: bool = Field(default=False, description="模型思考是否开启")
|
enable_thinking: bool = Field(default=False, description="模型思考是否开启")
|
||||||
|
|
||||||
call_print: bool = Field(default=False, description="是否直接调用 print 生成印花")
|
call_print: bool = Field(default=False, description="是否直接调用 print 生成印花")
|
||||||
@@ -22,6 +25,6 @@ class FashionAgentRequest(BaseModel):
|
|||||||
design_request_data: dict = Field(default={}, description="design 请求参数")
|
design_request_data: dict = Field(default={}, description="design 请求参数")
|
||||||
|
|
||||||
call_trending: bool = Field(default=False, description="是否直接调用 trending 趋势分析")
|
call_trending: bool = Field(default=False, description="是否直接调用 trending 趋势分析")
|
||||||
call_explor: bool = Field(default=False, description="是否直接调用 explorer 灵感探索")
|
call_explore: bool = Field(default=False, description="是否直接调用 explore 灵感探索")
|
||||||
|
|
||||||
provider: Optional[str] = Field(default="unsplash", description="图片源: pexels 或 unsplash")
|
provider: Optional[str] = Field(default="unsplash", description="图片源: pexels 或 unsplash")
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from langgraph.graph import END, START, StateGraph
|
|||||||
from langgraph.graph.message import add_messages
|
from langgraph.graph.message import add_messages
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from langchain_core.runnables import RunnableConfig
|
from langchain_core.runnables import RunnableConfig
|
||||||
from app.service.fashion_agent.graph_node.explorer_graph.tools import explor_tool
|
from app.service.fashion_agent.graph_node.explore_graph.tools import explore_tool
|
||||||
from app.service.fashion_agent.init_llm import build_llm
|
from app.service.fashion_agent.init_llm import qwen_plus_llm
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
@@ -18,10 +18,10 @@ logger = logging.getLogger()
|
|||||||
"""定义状态"""
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
class ExplorerState(TypedDict):
|
class exploreState(TypedDict):
|
||||||
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
input_text: str
|
input_text: str = ""
|
||||||
search_query: str
|
search_query: str = ""
|
||||||
image_results: list[dict] # 每项包含 image_url 和 minio_path
|
image_results: list[dict] # 每项包含 image_url 和 minio_path
|
||||||
provider: str = "unsplash" # 图片源: "pexels" 或 "unsplash"
|
provider: str = "unsplash" # 图片源: "pexels" 或 "unsplash"
|
||||||
|
|
||||||
@@ -29,71 +29,70 @@ class ExplorerState(TypedDict):
|
|||||||
"""节点"""
|
"""节点"""
|
||||||
|
|
||||||
|
|
||||||
def extract_input_node(state: ExplorerState) -> dict:
|
def extract_input_node(state: exploreState) -> dict:
|
||||||
"""从 messages 中提取用户输入"""
|
"""从 messages 中提取用户输入"""
|
||||||
input_text = state["messages"][0].content if state.get("messages") else ""
|
input_text = state["messages"][-1].content if state.get("messages") else input_text
|
||||||
return {"input_text": input_text}
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
class SearchQuery(BaseModel):
|
class SearchQuery(BaseModel):
|
||||||
"""搜索关键词"""
|
"""搜索关键词"""
|
||||||
|
|
||||||
query: str = Field(description="用于搜索灵感图片的英文关键词,简洁有力")
|
query: str = Field(description="用于搜索灵感图片的英文关键词,简洁有力")
|
||||||
|
|
||||||
|
|
||||||
# TODO 要考虑搜索图片失败或者图片不存在的情况, 搜索不到 需要调整搜索词或者拆分搜索词,最终失败的话调用mood board生成工具生成, 保证绝对有图片
|
# TODO 要考虑搜索图片失败或者图片不存在的情况, 搜索不到 需要调整搜索词或者拆分搜索词,最终失败的话调用mood board生成工具生成, 保证绝对有图片
|
||||||
async def generate_query_node(state: ExplorerState) -> dict:
|
async def generate_query_node(state: exploreState) -> dict:
|
||||||
"""使用 LLM 分析用户输入,生成搜索关键词"""
|
"""使用 LLM 分析用户输入,生成搜索关键词"""
|
||||||
input_text = state["input_text"]
|
input_text = state["input_text"]
|
||||||
logger.info(f"[Explorer] 用户输入: {input_text}")
|
logger.info(f"[explore] 用户输入: {input_text}")
|
||||||
llm = build_llm()
|
|
||||||
|
|
||||||
structured_llm = llm.with_structured_output(SearchQuery)
|
structured_llm = qwen_plus_llm.with_structured_output(SearchQuery)
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
SystemMessage(content="""你是一个专业的服装设计师助手。
|
SystemMessage(content="""你是专业服装设计师助手.
|
||||||
根据用户输入,生成一个英文搜索关键词,用于在图片库中搜索服装设计灵感图片(moodboard)。
|
根据用户中文需求,生成适合时尚灵感图(moodboard)图库搜索的英文关键词短句.
|
||||||
|
|
||||||
要求:
|
严格输出规则:
|
||||||
1. 使用英文,简洁有力
|
1. 必须返回标准JSON对象,**禁止输出任何额外文字,解释,思考,前言后语**;
|
||||||
2. 适合搜索高质量的设计灵感图片
|
2. JSON 只包含一个字段 "query",值为简洁英文搜索词;
|
||||||
|
3. 关键词简洁,适配高清时尚素材搜索;
|
||||||
|
|
||||||
例如:
|
输出格式示例(仅允许输出如下JSON,不要加别的内容):
|
||||||
用户输入:"夏季连衣裙,清新风格"
|
{"query": "summer dress fresh style"}"""),
|
||||||
输出:summer dress fresh style"""),
|
|
||||||
HumanMessage(content=input_text),
|
HumanMessage(content=input_text),
|
||||||
]
|
]
|
||||||
|
|
||||||
result = structured_llm.invoke(messages)
|
result = structured_llm.invoke(messages)
|
||||||
logger.info(f"[Explorer] LLM 生成的搜索关键词: {result.query}")
|
search_query = result.query
|
||||||
return {"search_query": result.query}
|
logger.info(f"[explore] LLM 生成的搜索关键词: {search_query}")
|
||||||
|
return {"search_query": search_query}
|
||||||
|
|
||||||
|
|
||||||
async def search_and_upload_node(state: ExplorerState, config: RunnableConfig) -> dict:
|
async def search_and_upload_node(state: exploreState, config: RunnableConfig) -> dict:
|
||||||
"""使用搜索关键词获取图片并上传到 minio"""
|
"""使用搜索关键词获取图片并上传到 minio"""
|
||||||
query = state.get("search_query", "")
|
query = state.get("search_query", "")
|
||||||
user_id = state.get("user_id", "agent")
|
user_id = state.get("user_id", "agent")
|
||||||
provider = state.get("provider", "unsplash")
|
provider = state.get("provider", "unsplash")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
results = await explor_tool.ainvoke({"query": query, "per_page": 4, "user_id": user_id, "method": provider}, config=config)
|
results = await explore_tool.ainvoke({"query": query, "per_page": 4, "user_id": user_id, "method": provider}, config=config)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[Explorer] 搜索失败 '{query}': {e}")
|
logger.error(f"[explore] 搜索失败 '{query}': {e}")
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
return {"image_results": results}
|
return {"image_results": results}
|
||||||
|
|
||||||
|
|
||||||
def summarize_node(state: ExplorerState) -> dict:
|
def summarize_node(state: exploreState) -> dict:
|
||||||
"""汇总结果"""
|
"""汇总结果"""
|
||||||
input_text = state.get("input_text", "")
|
input_text = state.get("input_text", "")
|
||||||
query = state.get("search_query", "")
|
query = state.get("search_query", "")
|
||||||
results = state.get("image_results", [])
|
results = state.get("image_results", [])
|
||||||
|
|
||||||
result_text = f"【灵感探索 Moodboard】\n\n"
|
result_text = f"[灵感探索 Moodboard]\n\n"
|
||||||
result_text += f"基于您的需求:「{input_text}」\n"
|
result_text += f"基于您的需求: {input_text}\n\n"
|
||||||
result_text += f"搜索关键词:{query}\n\n"
|
result_text += f"搜索关键词:{query}\n\n"
|
||||||
result_text += f"已为您找到 {len(results)} 张灵感图片:\n"
|
result_text += f"已为您找到 {len(results)} 张灵感图片:\n"
|
||||||
|
|
||||||
for i, item in enumerate(results, 1):
|
for i, item in enumerate(results, 1):
|
||||||
result_text += f" {i}. 原图: {item.get('image_url', '')}\n"
|
result_text += f" {i}. 原图: {item.get('image_url', '')}\n"
|
||||||
@@ -105,9 +104,9 @@ def summarize_node(state: ExplorerState) -> dict:
|
|||||||
"""构建图"""
|
"""构建图"""
|
||||||
|
|
||||||
|
|
||||||
def build_explorer_graph():
|
def build_explore_graph():
|
||||||
"""构建灵感探索图"""
|
"""构建灵感探索图"""
|
||||||
workflow = StateGraph(ExplorerState)
|
workflow = StateGraph(exploreState)
|
||||||
|
|
||||||
workflow.add_node("extract_input", extract_input_node)
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
workflow.add_node("generate_query", generate_query_node)
|
workflow.add_node("generate_query", generate_query_node)
|
||||||
@@ -126,10 +125,10 @@ def build_explorer_graph():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
async def test():
|
async def test():
|
||||||
graph = build_explorer_graph()
|
graph = build_explore_graph()
|
||||||
result = await graph.ainvoke(
|
result = await graph.ainvoke(
|
||||||
{
|
{
|
||||||
"messages": [HumanMessage(content="夏季连衣裙,清新自然风格")],
|
"messages": [HumanMessage(content="夏季连衣裙,清新自然风格")],
|
||||||
"provider": "unsplash",
|
"provider": "unsplash",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -15,7 +15,7 @@ class SearchInput(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
@tool(args_schema=SearchInput)
|
@tool(args_schema=SearchInput)
|
||||||
async def explor_tool(
|
async def explore_tool(
|
||||||
query: str, per_page: int = 4, user_id: str = "agent", method: str = "unsplash", config: RunnableConfig = None
|
query: str, per_page: int = 4, user_id: str = "agent", method: str = "unsplash", config: RunnableConfig = None
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
"""Search for fashion inspiration images on Unsplash and upload to minio. Returns a list of dicts with image_url and minio_path."""
|
"""Search for fashion inspiration images on Unsplash and upload to minio. Returns a list of dicts with image_url and minio_path."""
|
||||||
@@ -7,7 +7,7 @@ from langgraph.graph import END, START, StateGraph
|
|||||||
from langgraph.graph.message import add_messages
|
from langgraph.graph.message import add_messages
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from app.service.fashion_agent.graph_node.logo_graph.tools import generate_logo_tool
|
from app.service.fashion_agent.graph_node.logo_graph.tools import generate_logo_tool
|
||||||
from app.service.fashion_agent.init_llm import qwen_plus_llm
|
from app.service.fashion_agent.init_llm import build_llm, qwen_plus_llm
|
||||||
|
|
||||||
"""初始化 LLM TODO 将 API Key 替换为环境变量或者配置文件中的值,避免在代码中硬编码敏感信息"""
|
"""初始化 LLM TODO 将 API Key 替换为环境变量或者配置文件中的值,避免在代码中硬编码敏感信息"""
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ from app.service.fashion_agent.init_llm import qwen_plus_llm
|
|||||||
|
|
||||||
class LogoState(TypedDict):
|
class LogoState(TypedDict):
|
||||||
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
input_text: str
|
input_text: str = ""
|
||||||
user_id: str = "agent"
|
user_id: str = "agent"
|
||||||
role: str = ""
|
role: str = ""
|
||||||
gender: str = ""
|
gender: str = ""
|
||||||
@@ -35,14 +35,14 @@ class LogoState(TypedDict):
|
|||||||
|
|
||||||
# 定义输出结构
|
# 定义输出结构
|
||||||
class LogoPrompt(BaseModel):
|
class LogoPrompt(BaseModel):
|
||||||
"""生成的 Logo 图像提示词"""
|
"""logo image generation diagram prompt words"""
|
||||||
|
|
||||||
prompts: list[str] = Field(description="用于生成 Logo 的详细提示词")
|
prompts: list[str] = Field(description="Array of prompt words, simple English words")
|
||||||
|
|
||||||
|
|
||||||
def extract_input_node(state: LogoState) -> dict:
|
def extract_input_node(state: LogoState) -> dict:
|
||||||
"""从 messages 中提取用户输入"""
|
"""从 messages 中提取用户输入"""
|
||||||
input_text = state["messages"][0].content if state.get("messages") else ""
|
input_text = state["messages"][-1].content if state.get("messages") else state["input_text"]
|
||||||
return {"input_text": input_text}
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
@@ -51,18 +51,19 @@ def generate_logo_prompt_node(state: LogoState) -> dict:
|
|||||||
structured_llm = qwen_plus_llm.with_structured_output(LogoPrompt)
|
structured_llm = qwen_plus_llm.with_structured_output(LogoPrompt)
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
SystemMessage(content="""从用户输入中提取核心主题词,只输出一个简单的英文单词。
|
SystemMessage(content="""从用户输入中提取核心主题词,用一个简单的英文单词作为 prompts 字段的值。
|
||||||
例如:
|
例如:
|
||||||
- "我想要一个猫咪图案" -> "cat"
|
- "我想要一个猫咪图案" -> {"prompts": ["cat"]}
|
||||||
- "设计一个花朵" -> "flower"
|
- "设计一个花朵" -> {"prompts": ["flower"]}
|
||||||
- "可爱的狗" -> "dog"
|
- "可爱的狗" -> {"prompts": ["dog"]}
|
||||||
只输出单词,不要其他内容。"""),
|
请严格按照 JSON 格式输出,包含 prompts 字段。
|
||||||
|
"""),
|
||||||
HumanMessage(content=state["input_text"]),
|
HumanMessage(content=state["input_text"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
result = structured_llm.invoke(messages)
|
result = structured_llm.invoke(messages)
|
||||||
prompts = result.prompts
|
prompts = result.prompts
|
||||||
|
print(result)
|
||||||
return {
|
return {
|
||||||
"logo_prompts": prompts,
|
"logo_prompts": prompts,
|
||||||
}
|
}
|
||||||
@@ -139,14 +140,14 @@ async def main(test_input, user_id="agent", need_prompt_generation=True):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 测试示例 1: 需要 prompt 生成(默认)- 简单关键词输入
|
|
||||||
test_input = "我想要一个金毛图案"
|
|
||||||
result = asyncio.run(main(test_input, need_prompt_generation=True))
|
|
||||||
print("=== 需要 prompt 生成 ===")
|
|
||||||
print(f"Result: {result}")
|
|
||||||
|
|
||||||
# 测试示例 2: 直接使用用户提供的 prompt
|
async def test():
|
||||||
user_prompt = "golden retriever"
|
graph = build_logo_graph()
|
||||||
result = asyncio.run(main(user_prompt, need_prompt_generation=False))
|
result = await graph.ainvoke(
|
||||||
print("\n=== 直接使用 prompt ===")
|
{
|
||||||
print(f"Result: {result}")
|
"messages": [HumanMessage(content="我想要一个金毛图案")],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(result["messages"][-1].content)
|
||||||
|
|
||||||
|
asyncio.run(test())
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import httpx
|
|||||||
async def generate_image(
|
async def generate_image(
|
||||||
bucket_name="fida-public-bucket",
|
bucket_name="fida-public-bucket",
|
||||||
object_name=f"furniture/sketches/123456.png",
|
object_name=f"furniture/sketches/123456.png",
|
||||||
|
input_images=[],
|
||||||
prompt="Generate a modern minimalist dining chair made of light "
|
prompt="Generate a modern minimalist dining chair made of light "
|
||||||
"oak wood and white leather, with slim metal legs, photographed "
|
"oak wood and white leather, with slim metal legs, photographed "
|
||||||
"in a bright Scandinavian living room with natural sunlight, high detail, "
|
"in a bright Scandinavian living room with natural sunlight, high detail, "
|
||||||
"8k resolution.",
|
"8k resolution.",
|
||||||
):
|
):
|
||||||
request_data = {
|
request_data = {
|
||||||
"input_image_paths": [],
|
"input_image_paths": input_images,
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
"bucket_name": bucket_name,
|
"bucket_name": bucket_name,
|
||||||
"object_name": object_name,
|
"object_name": object_name,
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ async def search_photos(query: str, per_page: int = 4, user_id: str = "agent") -
|
|||||||
# 上传到 minio(使用线程池避免阻塞事件循环)
|
# 上传到 minio(使用线程池避免阻塞事件循环)
|
||||||
file_name = f"{uuid7()}.jpg"
|
file_name = f"{uuid7()}.jpg"
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
minio_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "explorer", file_name)
|
minio_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "explore", file_name)
|
||||||
results.append({"image_url": image_url, "minio_path": minio_url})
|
results.append({"image_url": image_url, "minio_path": minio_url})
|
||||||
logger.info(f"[Explorer] 上传成功: {minio_url}")
|
logger.info(f"[explore] 上传成功: {minio_url}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[Explorer] 上传失败: {e}")
|
logger.error(f"[explore] 上传失败: {e}")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ async def get_random_photos(query: str, count: int = 4, user_id: str = "agent")
|
|||||||
# 上传到 minio(使用线程池避免阻塞事件循环)
|
# 上传到 minio(使用线程池避免阻塞事件循环)
|
||||||
file_name = f"{uuid7()}.jpg"
|
file_name = f"{uuid7()}.jpg"
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
minio_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "explorer", file_name)
|
minio_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "explore", file_name)
|
||||||
results.append({"image_url": image_url, "minio_path": minio_url})
|
results.append({"image_url": image_url, "minio_path": minio_url})
|
||||||
logger.info(f"[Explorer] 上传成功: {minio_url}")
|
logger.info(f"[explore] 上传成功: {minio_url}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[Explorer] 上传失败: {e}")
|
logger.error(f"[explore] 上传失败: {e}")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class PrintPrompt(BaseModel):
|
|||||||
|
|
||||||
def extract_input_node(state: PrintState) -> dict:
|
def extract_input_node(state: PrintState) -> dict:
|
||||||
"""从 messages 中提取用户输入"""
|
"""从 messages 中提取用户输入"""
|
||||||
input_text = state["messages"][0].content if state.get("messages") else ""
|
input_text = state["messages"][-1].content if state.get("messages") else ""
|
||||||
return {"input_text": input_text}
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,13 +49,13 @@ def generate_print_prompt_node(state: PrintState) -> dict:
|
|||||||
structured_llm = qwen_plus_llm.with_structured_output(PrintPrompt)
|
structured_llm = qwen_plus_llm.with_structured_output(PrintPrompt)
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
SystemMessage(content=f"""你是一个专业的印花图案设计师。
|
SystemMessage(content=f"""你是一个专业的印花图案设计师.
|
||||||
请根据用户输入,生成用于AI图像生成的印花图案提示词。
|
请根据用户输入,生成用于AI图像生成的印花图案提示词.
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
1. 提示词应该详细描述印花图案的样式、元素、颜色、布局
|
1. 提示词应该详细描述印花图案的样式,元素,颜色,布局
|
||||||
2. 提示词应该适合用于 Stable Diffusion 图像生成模型
|
2. 提示词应该适合用于 Stable Diffusion 图像生成模型
|
||||||
3. 提示词应该使用英文,因为图像生成模型对英文理解更好
|
3. 提示词应该使用英文,因为图像生成模型对英文理解更好
|
||||||
4. 提示词数量为 {state.get("print_num", 1)}
|
4. 提示词数量为 {state.get("print_num", 1)}
|
||||||
"""),
|
"""),
|
||||||
HumanMessage(content=state["input_text"]),
|
HumanMessage(content=state["input_text"]),
|
||||||
@@ -73,17 +73,19 @@ def generate_print_prompt_node(state: PrintState) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
async def generate_print_img_node(state: PrintState) -> dict:
|
async def generate_print_img_node(state: PrintState) -> dict:
|
||||||
"""根据生成的提示词,生成印花图案"""
|
"""根据生成的提示词,生成印花图案"""
|
||||||
# 如果 print_prompts 为空,使用 input_text 作为 prompt
|
# 如果 print_prompts 为空,使用 input_text 作为 prompt
|
||||||
if state.get("print_need_prompt_generation", False):
|
if state.get("print_need_prompt_generation", False):
|
||||||
prompts = state["print_prompts"] if state["print_prompts"] else [state["input_text"]]
|
prompts = state["print_prompts"] if state["print_prompts"] else [state["input_text"]]
|
||||||
else:
|
else:
|
||||||
input_text = state.get("input_text", "")
|
input_text = state.get("input_text", "")
|
||||||
prompts = [input_text]
|
prompts = [input_text]
|
||||||
|
|
||||||
|
input_images = state.get("input_images", [])
|
||||||
|
|
||||||
print_img_urls = []
|
print_img_urls = []
|
||||||
for prompt in prompts:
|
for prompt in prompts:
|
||||||
image_url = await generate_print_tool.ainvoke({"prompt": prompt})
|
image_url = await generate_print_tool.ainvoke({"prompt": prompt, "input_images": input_images})
|
||||||
print_img_urls.append(image_url)
|
print_img_urls.append(image_url)
|
||||||
logger.info(f"[Print Graph] Generated print image URL: {image_url}")
|
logger.info(f"[Print Graph] Generated print image URL: {image_url}")
|
||||||
|
|
||||||
@@ -94,7 +96,7 @@ async def generate_print_img_node(state: PrintState) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def should_generate_prompt(state: PrintState) -> str:
|
def should_generate_prompt(state: PrintState) -> str:
|
||||||
"""条件分支:判断是否需要生成 prompt"""
|
"""条件分支:判断是否需要生成 prompt"""
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[Print Graph] should_generate_prompt: print_need_prompt_generation={state.get('print_need_prompt_generation')}, print_prompts={state.get('print_prompts')}"
|
f"[Print Graph] should_generate_prompt: print_need_prompt_generation={state.get('print_need_prompt_generation')}, print_prompts={state.get('print_prompts')}"
|
||||||
@@ -106,7 +108,6 @@ def should_generate_prompt(state: PrintState) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def build_print_graph():
|
def build_print_graph():
|
||||||
|
|
||||||
workflow = StateGraph(PrintState)
|
workflow = StateGraph(PrintState)
|
||||||
workflow.add_node("extract_input", extract_input_node)
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
workflow.add_node("gen_prompt", generate_print_prompt_node)
|
workflow.add_node("gen_prompt", generate_print_prompt_node)
|
||||||
@@ -145,8 +146,8 @@ async def main(test_input, print_need_prompt_generation=True):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 测试示例 1: 需要 prompt 生成(默认)
|
# 测试示例 1: 需要 prompt 生成(默认)
|
||||||
test_input = "我想要一个优雅的花卉印花,适合用于连衣裙,颜色以粉色和白色为主"
|
test_input = "我想要一个优雅的花卉印花,适合用于连衣裙,颜色以粉色和白色为主"
|
||||||
result = asyncio.run(main(test_input, print_need_prompt_generation=True))
|
result = asyncio.run(main(test_input, print_need_prompt_generation=True))
|
||||||
print("=== 需要 prompt 生成 ===")
|
print("=== 需要 prompt 生成 ===")
|
||||||
print(f"Result: {result}")
|
print(f"Result: {result}")
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ class GenerateImageToolInput(BaseModel):
|
|||||||
"""Input schema for the Generate Image Tool."""
|
"""Input schema for the Generate Image Tool."""
|
||||||
|
|
||||||
prompt: str = Field(description="Description of the desired image, e.g., 'A cozy living room with warm lighting and natural textures.'")
|
prompt: str = Field(description="Description of the desired image, e.g., 'A cozy living room with warm lighting and natural textures.'")
|
||||||
|
input_images: list[str] = Field(default=[], description="Input images for the generation.")
|
||||||
|
|
||||||
|
|
||||||
@tool(args_schema=GenerateImageToolInput)
|
@tool(args_schema=GenerateImageToolInput)
|
||||||
async def generate_print_tool(prompt: str) -> str:
|
async def generate_print_tool(prompt: str, input_images: list[str]) -> str:
|
||||||
"""Generate an image based on the provided prompt."""
|
"""Generate an image based on the provided prompt."""
|
||||||
|
|
||||||
bucket_name = "aida-users"
|
bucket_name = "aida-users"
|
||||||
object_name = f"agent_generate_print/{uuid7()}.png"
|
object_name = f"agent_generate_print/{uuid7()}.png"
|
||||||
image_url = await generate_image(prompt=prompt, bucket_name=bucket_name, object_name=object_name)
|
image_url = await generate_image(prompt=prompt, bucket_name=bucket_name, object_name=object_name, input_images=input_images)
|
||||||
return [image_url]
|
return [image_url]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class SketchPrompt(BaseModel):
|
|||||||
|
|
||||||
def extract_input_node(state: SketchState) -> dict:
|
def extract_input_node(state: SketchState) -> dict:
|
||||||
"""从 messages 中提取用户输入"""
|
"""从 messages 中提取用户输入"""
|
||||||
input_text = state["messages"][0].content if state.get("messages") else ""
|
input_text = state["messages"][-1].content if state.get("messages") else ""
|
||||||
return {"input_text": input_text}
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
@@ -80,15 +80,16 @@ async def generate_sketch_img_node(state: SketchState) -> dict:
|
|||||||
"""根据生成的提示词,生成服装草图"""
|
"""根据生成的提示词,生成服装草图"""
|
||||||
# 如果 sketch_need_prompt_generation=False 且 sketch_prompts 为空,使用模板生成 prompt
|
# 如果 sketch_need_prompt_generation=False 且 sketch_prompts 为空,使用模板生成 prompt
|
||||||
if not state.get("sketch_need_prompt_generation", False) and not state.get("sketch_prompts"):
|
if not state.get("sketch_need_prompt_generation", False) and not state.get("sketch_prompts"):
|
||||||
|
|
||||||
input_text = state.get("input_text", "")
|
input_text = state.get("input_text", "")
|
||||||
prompts = [build_sketch_template_prompt(input_text)]
|
prompts = [build_sketch_template_prompt(input_text)]
|
||||||
else:
|
else:
|
||||||
prompts = state["sketch_prompts"] if state["sketch_prompts"] else [state["input_text"]]
|
prompts = state["sketch_prompts"] if state["sketch_prompts"] else [state["input_text"]]
|
||||||
|
|
||||||
|
input_images = state.get("input_images", [])
|
||||||
|
|
||||||
sketch_img_urls = []
|
sketch_img_urls = []
|
||||||
for prompt in prompts:
|
for prompt in prompts:
|
||||||
image_url = await generate_sketch_tool.ainvoke({"prompt": prompt})
|
image_url = await generate_sketch_tool.ainvoke({"prompt": prompt, "input_images": input_images})
|
||||||
sketch_img_urls.append(image_url)
|
sketch_img_urls.append(image_url)
|
||||||
|
|
||||||
return {"sketch_img_urls": sketch_img_urls}
|
return {"sketch_img_urls": sketch_img_urls}
|
||||||
@@ -107,33 +108,26 @@ def should_generate_prompt(state: SketchState) -> str:
|
|||||||
|
|
||||||
def build_sketch_graph():
|
def build_sketch_graph():
|
||||||
workflow = StateGraph(SketchState)
|
workflow = StateGraph(SketchState)
|
||||||
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
|
workflow.add_node("gen_prompt", generate_sketch_prompt_node)
|
||||||
workflow.add_node("gen_sketch", generate_sketch_img_node)
|
workflow.add_node("gen_sketch", generate_sketch_img_node)
|
||||||
workflow.add_edge(START, "gen_sketch")
|
|
||||||
|
# 添加边
|
||||||
|
workflow.add_edge(START, "extract_input")
|
||||||
|
workflow.add_conditional_edges(
|
||||||
|
"extract_input",
|
||||||
|
should_generate_prompt,
|
||||||
|
{
|
||||||
|
"gen_prompt": "gen_prompt",
|
||||||
|
"gen_sketch": "gen_sketch",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
workflow.add_edge("gen_prompt", "gen_sketch")
|
||||||
workflow.add_edge("gen_sketch", END)
|
workflow.add_edge("gen_sketch", END)
|
||||||
|
|
||||||
graph = workflow.compile()
|
graph = workflow.compile()
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
# workflow = StateGraph(SketchState)
|
|
||||||
# workflow.add_node("extract_input", extract_input_node)
|
|
||||||
# workflow.add_node("gen_prompt", generate_sketch_prompt_node)
|
|
||||||
# workflow.add_node("gen_sketch", generate_sketch_img_node)
|
|
||||||
|
|
||||||
# # 添加边
|
|
||||||
# workflow.add_edge(START, "extract_input")
|
|
||||||
# workflow.add_conditional_edges(
|
|
||||||
# "extract_input",
|
|
||||||
# should_generate_prompt,
|
|
||||||
# {
|
|
||||||
# "gen_prompt": "gen_prompt",
|
|
||||||
# "gen_sketch": "gen_sketch",
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
# workflow.add_edge("gen_prompt", "gen_sketch")
|
|
||||||
# workflow.add_edge("gen_sketch", END)
|
|
||||||
|
|
||||||
# graph = workflow.compile()
|
|
||||||
# return graph
|
|
||||||
|
|
||||||
|
|
||||||
def build_sketch_template_prompt(input_text: str) -> str:
|
def build_sketch_template_prompt(input_text: str) -> str:
|
||||||
"""构建 sketch prompt 模板"""
|
"""构建 sketch prompt 模板"""
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ class GenerateImageToolInput(BaseModel):
|
|||||||
"""Input schema for the Generate Image Tool."""
|
"""Input schema for the Generate Image Tool."""
|
||||||
|
|
||||||
prompt: str = Field(description="Description of the desired image, e.g., 'A cozy living room with warm lighting and natural textures.'")
|
prompt: str = Field(description="Description of the desired image, e.g., 'A cozy living room with warm lighting and natural textures.'")
|
||||||
|
input_images: list[str] = Field(default=[], description="Input images for the generation.")
|
||||||
|
|
||||||
|
|
||||||
@tool(args_schema=GenerateImageToolInput)
|
@tool(args_schema=GenerateImageToolInput)
|
||||||
async def generate_sketch_tool(prompt: str) -> str:
|
async def generate_sketch_tool(prompt: str, input_images: list[str]) -> str:
|
||||||
"""Generate an image based on the provided prompt."""
|
"""Generate an image based on the provided prompt."""
|
||||||
|
|
||||||
bucket_name = "fida-public-bucket"
|
bucket_name = "fida-public-bucket"
|
||||||
object_name = f"test/{uuid7()}.png"
|
object_name = f"test/{uuid7()}.png"
|
||||||
image_url = await generate_image(prompt=prompt, bucket_name=bucket_name, object_name=object_name)
|
image_url = await generate_image(prompt=prompt, bucket_name=bucket_name, object_name=object_name, input_images=input_images)
|
||||||
return [image_url]
|
return [image_url]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Annotated, Required, TypedDict
|
from typing import Annotated, Required, TypedDict
|
||||||
from deepagents import CompiledSubAgent, create_deep_agent
|
|
||||||
from langchain.agents import create_agent
|
from langchain.agents import create_agent
|
||||||
from langchain.tools import tool
|
from langchain_core.messages import AnyMessage
|
||||||
from langchain_core.messages import AnyMessage, HumanMessage
|
|
||||||
from langchain_qwq import ChatQwen
|
|
||||||
from langgraph.graph import END, START, StateGraph
|
from langgraph.graph import END, START, StateGraph
|
||||||
from langgraph.graph.message import add_messages
|
from langgraph.graph.message import add_messages
|
||||||
|
|
||||||
from app.service.fashion_agent.graph_node.design_graph.graph import build_design_graph
|
from app.service.fashion_agent.graph_node.design_graph.graph import build_design_graph
|
||||||
from app.service.fashion_agent.graph_node.design_graph.tools import design_tool
|
from app.service.fashion_agent.graph_node.explore_graph.tools import explore_tool
|
||||||
from app.service.fashion_agent.graph_node.explorer_graph.tools import explor_tool
|
|
||||||
from app.service.fashion_agent.graph_node.logo_graph.graph import build_logo_graph
|
from app.service.fashion_agent.graph_node.logo_graph.graph import build_logo_graph
|
||||||
from app.service.fashion_agent.graph_node.logo_graph.tools import generate_logo_tool
|
from app.service.fashion_agent.graph_node.logo_graph.tools import generate_logo_tool
|
||||||
from app.service.fashion_agent.graph_node.print_graph.graph import build_print_graph
|
from app.service.fashion_agent.graph_node.print_graph.graph import build_print_graph
|
||||||
@@ -18,7 +13,7 @@ from app.service.fashion_agent.graph_node.print_graph.tools import generate_prin
|
|||||||
from app.service.fashion_agent.graph_node.sketch_graph.graph import build_sketch_graph
|
from app.service.fashion_agent.graph_node.sketch_graph.graph import build_sketch_graph
|
||||||
from app.service.fashion_agent.graph_node.sketch_graph.tools import generate_sketch_tool
|
from app.service.fashion_agent.graph_node.sketch_graph.tools import generate_sketch_tool
|
||||||
from app.service.fashion_agent.graph_node.trending_graph.trending_graph import build_trending_graph
|
from app.service.fashion_agent.graph_node.trending_graph.trending_graph import build_trending_graph
|
||||||
from app.service.fashion_agent.graph_node.explorer_graph.graph import build_explorer_graph
|
from app.service.fashion_agent.graph_node.explore_graph.graph import build_explore_graph
|
||||||
from app.service.fashion_agent.init_llm import build_llm
|
from app.service.fashion_agent.init_llm import build_llm
|
||||||
|
|
||||||
print_graph = build_print_graph()
|
print_graph = build_print_graph()
|
||||||
@@ -26,13 +21,16 @@ logo_graph = build_logo_graph()
|
|||||||
sketch_graph = build_sketch_graph()
|
sketch_graph = build_sketch_graph()
|
||||||
design_graph = build_design_graph()
|
design_graph = build_design_graph()
|
||||||
trending_graph = build_trending_graph()
|
trending_graph = build_trending_graph()
|
||||||
explorer_graph = build_explorer_graph()
|
explore_graph = build_explore_graph()
|
||||||
|
|
||||||
|
|
||||||
class MainState(TypedDict):
|
class MainState(TypedDict):
|
||||||
# 消息
|
# 消息
|
||||||
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
|
||||||
|
# 上传图片
|
||||||
|
input_images: list[str] = []
|
||||||
|
|
||||||
# 模块控制
|
# 模块控制
|
||||||
call_design: bool = False
|
call_design: bool = False
|
||||||
call_print: bool = False
|
call_print: bool = False
|
||||||
@@ -40,7 +38,7 @@ class MainState(TypedDict):
|
|||||||
call_sketch: bool = False
|
call_sketch: bool = False
|
||||||
call_design: bool = False
|
call_design: bool = False
|
||||||
call_trending: bool = False
|
call_trending: bool = False
|
||||||
call_explor: bool = False
|
call_explore: bool = False
|
||||||
|
|
||||||
# design参数
|
# design参数
|
||||||
design_request_data: dict = {}
|
design_request_data: dict = {}
|
||||||
@@ -58,7 +56,7 @@ class MainState(TypedDict):
|
|||||||
print_img_urls: list[str] = []
|
print_img_urls: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
tools = [explor_tool, generate_logo_tool, generate_print_tool, generate_sketch_tool]
|
tools = [explore_tool, generate_logo_tool, generate_print_tool, generate_sketch_tool]
|
||||||
|
|
||||||
|
|
||||||
def route_node(state: MainState) -> str:
|
def route_node(state: MainState) -> str:
|
||||||
@@ -73,12 +71,12 @@ def route_node(state: MainState) -> str:
|
|||||||
return "direct_design"
|
return "direct_design"
|
||||||
if state.get("call_trending"):
|
if state.get("call_trending"):
|
||||||
return "direct_trending"
|
return "direct_trending"
|
||||||
if state.get("call_explor"):
|
if state.get("call_explore"):
|
||||||
return "direct_explor"
|
return "direct_explore"
|
||||||
return "llm_agent"
|
return "llm_agent"
|
||||||
|
|
||||||
|
|
||||||
def build_main_graph(enable_thinking: bool = False):
|
async def build_main_graph(enable_thinking: bool = False, checkpointer=None):
|
||||||
llm = build_llm(enable_thinking=enable_thinking)
|
llm = build_llm(enable_thinking=enable_thinking)
|
||||||
chat_agent = create_agent(
|
chat_agent = create_agent(
|
||||||
model=llm, tools=tools, state_schema=MainState, system_prompt="你是一个专业的服装设计助手。根据用户需求,调用合适的工具完成任务."
|
model=llm, tools=tools, state_schema=MainState, system_prompt="你是一个专业的服装设计助手。根据用户需求,调用合适的工具完成任务."
|
||||||
@@ -94,7 +92,7 @@ def build_main_graph(enable_thinking: bool = False):
|
|||||||
workflow.add_node("direct_sketch", sketch_graph)
|
workflow.add_node("direct_sketch", sketch_graph)
|
||||||
workflow.add_node("direct_design", design_graph)
|
workflow.add_node("direct_design", design_graph)
|
||||||
workflow.add_node("direct_trending", trending_graph)
|
workflow.add_node("direct_trending", trending_graph)
|
||||||
workflow.add_node("direct_explor", explorer_graph)
|
workflow.add_node("direct_explore", explore_graph)
|
||||||
|
|
||||||
# 条件分支
|
# 条件分支
|
||||||
workflow.add_conditional_edges(
|
workflow.add_conditional_edges(
|
||||||
@@ -107,7 +105,7 @@ def build_main_graph(enable_thinking: bool = False):
|
|||||||
"direct_sketch": "direct_sketch",
|
"direct_sketch": "direct_sketch",
|
||||||
"direct_design": "direct_design",
|
"direct_design": "direct_design",
|
||||||
"direct_trending": "direct_trending",
|
"direct_trending": "direct_trending",
|
||||||
"direct_explor": "direct_explor",
|
"direct_explore": "direct_explore",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,27 +116,7 @@ def build_main_graph(enable_thinking: bool = False):
|
|||||||
workflow.add_edge("direct_sketch", END)
|
workflow.add_edge("direct_sketch", END)
|
||||||
workflow.add_edge("direct_design", END)
|
workflow.add_edge("direct_design", END)
|
||||||
workflow.add_edge("direct_trending", END)
|
workflow.add_edge("direct_trending", END)
|
||||||
workflow.add_edge("direct_explor", END)
|
workflow.add_edge("direct_explore", END)
|
||||||
|
|
||||||
return workflow.compile()
|
graph = workflow.compile(checkpointer=checkpointer)
|
||||||
|
return graph
|
||||||
|
|
||||||
agent = build_main_graph()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
async def test_direct():
|
|
||||||
# 直接调用 sketch,跳过 LLM
|
|
||||||
result = await agent.ainvoke(
|
|
||||||
{
|
|
||||||
"messages": [HumanMessage(content="我想设计衬衫,带有猫咪的图案")],
|
|
||||||
"call_sketch": True,
|
|
||||||
"sketch_need_prompt_generation": False,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
print("=== 直接调用 sketch ===")
|
|
||||||
print(result["messages"][-1].content)
|
|
||||||
|
|
||||||
asyncio.run(test_direct())
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
from langgraph.stream import ProtocolEvent, StreamChannel, StreamTransformer
|
from langgraph.stream import ProtocolEvent, StreamChannel, StreamTransformer
|
||||||
from app.service.fashion_agent.main_agent import build_main_graph
|
from app.service.fashion_agent.main_agent import build_main_graph
|
||||||
from langgraph.prebuilt import ToolCallTransformer
|
from langgraph.prebuilt import ToolCallTransformer
|
||||||
from typing import AsyncGenerator, TypedDict
|
from typing import AsyncGenerator
|
||||||
from langchain_core.messages import HumanMessage, ToolMessage
|
from langchain_core.messages import HumanMessage, ToolMessage
|
||||||
from app.schemas.fashion_agent import FashionAgentRequest
|
from app.schemas.fashion_agent import FashionAgentRequest
|
||||||
|
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
@@ -33,17 +32,21 @@ class FashionAgentService:
|
|||||||
async def run_stream(self, request: FashionAgentRequest) -> AsyncGenerator[str, None]:
|
async def run_stream(self, request: FashionAgentRequest) -> AsyncGenerator[str, None]:
|
||||||
"""流式运行 agent - 使用 v3 projections"""
|
"""流式运行 agent - 使用 v3 projections"""
|
||||||
|
|
||||||
config = {"configurable": {"user_id": request.user_id}}
|
config = {"configurable": {"thread_id": request.thread_id, "user_id": request.user_id}}
|
||||||
|
|
||||||
|
async with AsyncPostgresSaver.from_conn_string("postgresql://postgres:Aidlab123123!@20.1.1.43:15432/myapp_prod") as checkpointer:
|
||||||
|
await checkpointer.setup()
|
||||||
|
agent = await build_main_graph(enable_thinking=request.enable_thinking, checkpointer=checkpointer)
|
||||||
|
|
||||||
agent = build_main_graph(enable_thinking=request.enable_thinking)
|
|
||||||
state = {
|
state = {
|
||||||
"messages": [HumanMessage(content=request.message)],
|
"messages": [HumanMessage(content=request.message)],
|
||||||
|
"input_images": request.input_images,
|
||||||
"call_print": request.call_print,
|
"call_print": request.call_print,
|
||||||
"call_logo": request.call_logo,
|
"call_logo": request.call_logo,
|
||||||
"call_sketch": request.call_sketch,
|
"call_sketch": request.call_sketch,
|
||||||
"call_design": request.call_design,
|
"call_design": request.call_design,
|
||||||
"call_trending": request.call_trending,
|
"call_trending": request.call_trending,
|
||||||
"call_explor": request.call_explor,
|
"call_explore": request.call_explore,
|
||||||
"print_need_prompt_generation": request.print_need_prompt_generation,
|
"print_need_prompt_generation": request.print_need_prompt_generation,
|
||||||
"sketch_need_prompt_generation": request.sketch_need_prompt_generation,
|
"sketch_need_prompt_generation": request.sketch_need_prompt_generation,
|
||||||
"design_request_data": request.design_request_data,
|
"design_request_data": request.design_request_data,
|
||||||
@@ -73,6 +76,7 @@ class FashionAgentService:
|
|||||||
|
|
||||||
if isinstance(data["output"], ToolMessage):
|
if isinstance(data["output"], ToolMessage):
|
||||||
data["output"] = json.loads(data["output"].content)
|
data["output"] = json.loads(data["output"].content)
|
||||||
|
|
||||||
response_event = {"event_type": "tool", "data": data}
|
response_event = {"event_type": "tool", "data": data}
|
||||||
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
@@ -82,22 +86,27 @@ class FashionAgentService:
|
|||||||
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
elif event["method"] == "messages":
|
elif event["method"] == "messages":
|
||||||
data = event["params"]["data"][0]
|
event_data = event["params"]["data"]
|
||||||
|
data = event_data[0] if len(event_data) > 0 else {}
|
||||||
|
# 提取元数据 (如果有的话)
|
||||||
|
metadata = event_data[1] if len(event_data) > 1 else {}
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
continue
|
continue
|
||||||
if data.get("event") != "content-block-delta":
|
if metadata.get("langgraph_node") in {"gen_prompt", "generate_query"}:
|
||||||
continue
|
continue
|
||||||
block = data.get("delta") or {}
|
|
||||||
|
|
||||||
if block.get("type") == "text-delta":
|
ev = data.get("event")
|
||||||
response_event = {"event_type": "messages", "data": {"event": data["event"]} | block}
|
|
||||||
|
if ev == "content-block-delta":
|
||||||
|
block = data.get("delta") or {}
|
||||||
|
if block.get("type") in ("text-delta", "reasoning-delta"):
|
||||||
|
response_event = {"event_type": "messages", "data": {"event": ev} | block}
|
||||||
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
elif block.get("type") == "reasoning-delta":
|
|
||||||
response_event = {"event_type": "messages", "data": {"event": data["event"]} | block}
|
elif ev in ("message-start", "content-block-start", "content-block-finish", "message-finish"):
|
||||||
|
response_event = {"event_type": "messages", "data": {"event": ev} | data}
|
||||||
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
else:
|
|
||||||
pass
|
|
||||||
# print(f"----------------{event}")
|
|
||||||
response_event = {"event_type": "done"}
|
response_event = {"event_type": "done"}
|
||||||
yield f"data: {response_event}"
|
yield f"data: {response_event}"
|
||||||
|
|
||||||
@@ -117,13 +126,15 @@ if __name__ == "__main__":
|
|||||||
print("测试流式输出")
|
print("测试流式输出")
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
request = FashionAgentRequest(
|
request = FashionAgentRequest(
|
||||||
message="生成一张草莓图案",
|
thread_id="zhh",
|
||||||
call_print=True,
|
message="落日",
|
||||||
|
# call_print=True,
|
||||||
|
# input_images=["test/53d38bd5-f77b-4034-ada2-45f1e2ebe00c.png"],
|
||||||
# print_need_prompt_generation=False,
|
# print_need_prompt_generation=False,
|
||||||
# call_sketch=True,
|
# call_sketch=True,
|
||||||
# sketch_need_prompt_generation=False,
|
# sketch_need_prompt_generation=False,
|
||||||
# call_logo=True,
|
# call_logo=True,
|
||||||
# call_explor=True,
|
call_explore=True,
|
||||||
# call_design=True,
|
# call_design=True,
|
||||||
# design_request_data=request_data,
|
# design_request_data=request_data,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ dependencies = [
|
|||||||
"fastapi[standard]>=0.125.0",
|
"fastapi[standard]>=0.125.0",
|
||||||
"image>=1.5.33",
|
"image>=1.5.33",
|
||||||
"langchain>=1.2.0",
|
"langchain>=1.2.0",
|
||||||
|
"langchain-anthropic>=1.4.4",
|
||||||
"langchain-community>=0.4.1",
|
"langchain-community>=0.4.1",
|
||||||
|
"langchain-core>=1.4.2",
|
||||||
|
"langchain-ollama>=1.1.0",
|
||||||
"langchain-openai>=1.2.2",
|
"langchain-openai>=1.2.2",
|
||||||
"langchain-qwq>=0.3.5",
|
"langchain-qwq>=0.3.5",
|
||||||
"langgraph>=1.0.5",
|
"langgraph>=1.0.5",
|
||||||
"langgraph-api>=0.4.28",
|
"langgraph-api>=0.4.28",
|
||||||
|
"langgraph-checkpoint-postgres>=3.1.0",
|
||||||
"langgraph-cli[inmem,redis]<=0.4.26",
|
"langgraph-cli[inmem,redis]<=0.4.26",
|
||||||
"langsmith>=0.8.11",
|
"langsmith>=0.8.11",
|
||||||
"load>=1.0.14",
|
"load>=1.0.14",
|
||||||
@@ -40,6 +44,7 @@ dependencies = [
|
|||||||
"pandas-stubs~=2.3.3",
|
"pandas-stubs~=2.3.3",
|
||||||
"pika>=1.3.2",
|
"pika>=1.3.2",
|
||||||
"pillow>=12.0.0",
|
"pillow>=12.0.0",
|
||||||
|
"psycopg[binary,pool]>=3.3.4",
|
||||||
"pyasyncore>=1.0.4",
|
"pyasyncore>=1.0.4",
|
||||||
"pydantic>=2.12.5",
|
"pydantic>=2.12.5",
|
||||||
"pydantic-core>=2.41.5",
|
"pydantic-core>=2.41.5",
|
||||||
|
|||||||
89
uv.lock
generated
89
uv.lock
generated
@@ -1799,6 +1799,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/2a/a2/5feaf21cfe6fac80eae944f3ac5348d9e5e986813256f74f8dd104617474/langchain_google_genai-4.2.4-py3-none-any.whl", hash = "sha256:0e2c1021a15c91e60b68d813bb3e793bd1d9396b3f8639b943ab4e56e5652e04", size = 68832, upload-time = "2026-05-28T21:22:59.291Z" },
|
{ url = "https://files.pythonhosted.org/packages/2a/a2/5feaf21cfe6fac80eae944f3ac5348d9e5e986813256f74f8dd104617474/langchain_google_genai-4.2.4-py3-none-any.whl", hash = "sha256:0e2c1021a15c91e60b68d813bb3e793bd1d9396b3f8639b943ab4e56e5652e04", size = 68832, upload-time = "2026-05-28T21:22:59.291Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "langchain-ollama"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "langchain-core" },
|
||||||
|
{ name = "ollama" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d4/9b/6641afe8a5bf807e454fd464eddfc7eb2f2df53cb0b29744381171f9c609/langchain_ollama-1.1.0.tar.gz", hash = "sha256:f776f56f6782ae4da7692579b94a6575906118318d1023b455d7207f9d059811", size = 133075, upload-time = "2026-04-07T02:48:00.873Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/b2/c2acb076590a98bee2816ed5f285e00df162a34238f9e276e175e14ebc35/langchain_ollama-1.1.0-py3-none-any.whl", hash = "sha256:43ac83a6eacb0f43855810739794dd55019e0d9b17bdcf3ecb3b1991ac3b59dd", size = 31413, upload-time = "2026-04-07T02:47:59.642Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-openai"
|
name = "langchain-openai"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -1919,6 +1932,21 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/bd/b4/71425e3e38be92611300b9cc5e46a5bf98ab23f5ea8a75b73d02a2f1413c/langgraph_checkpoint-4.1.1-py3-none-any.whl", hash = "sha256:25d29144b082827218e7bc3f1e9b0566a4bb007895cd6cc26f66a8428739f56e", size = 56212, upload-time = "2026-05-22T16:57:37.203Z" },
|
{ url = "https://files.pythonhosted.org/packages/bd/b4/71425e3e38be92611300b9cc5e46a5bf98ab23f5ea8a75b73d02a2f1413c/langgraph_checkpoint-4.1.1-py3-none-any.whl", hash = "sha256:25d29144b082827218e7bc3f1e9b0566a4bb007895cd6cc26f66a8428739f56e", size = 56212, upload-time = "2026-05-22T16:57:37.203Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "langgraph-checkpoint-postgres"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "langgraph-checkpoint" },
|
||||||
|
{ name = "orjson" },
|
||||||
|
{ name = "psycopg" },
|
||||||
|
{ name = "psycopg-pool" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/6d/51/5a2dc42e8b5d5942b933b5b7237eae5a4dbc92508a04727c263dd383ad8a/langgraph_checkpoint_postgres-3.1.0.tar.gz", hash = "sha256:02bff4ab63d9dae8eab3a9640fce1d479da8965c9fba7b0dc04cb1f7c56f0a55", size = 148473, upload-time = "2026-05-12T03:40:10.599Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/cd/eff9b82bc3b5f62d481b437099f44f3ef7b1d907f166fb4ee25e8f84a1e7/langgraph_checkpoint_postgres-3.1.0-py3-none-any.whl", hash = "sha256:814cce2ef35d792bf07b090a95eed004f1acac0724fe6605536b13f6d1e7032c", size = 48988, upload-time = "2026-05-12T03:40:08.925Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langgraph-cli"
|
name = "langgraph-cli"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
@@ -2831,6 +2859,57 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
|
{ url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psycopg"
|
||||||
|
version = "3.3.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/db/2f/cb91e5502ec9de1de6f1b76cfbf69531932725361168bb06963620c77e2e/psycopg-3.3.4.tar.gz", hash = "sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc", size = 165799, upload-time = "2026-05-01T23:31:55.179Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/e0/7b3dee031daae7743609ce3c746565d4a3ed7c2c186479eb48e34e838c64/psycopg-3.3.4-py3-none-any.whl", hash = "sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a", size = 213001, upload-time = "2026-05-01T23:20:50.816Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
binary = [
|
||||||
|
{ name = "psycopg-binary", marker = "implementation_name != 'pypy'" },
|
||||||
|
]
|
||||||
|
pool = [
|
||||||
|
{ name = "psycopg-pool" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psycopg-binary"
|
||||||
|
version = "3.3.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/7d/03818e13ba7f36de93573c93ee3482006d3dfa8b0f8d28df511bad0a1a92/psycopg_binary-3.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5ab28a2a7649df3b72e6b674b4c190e448e8e77cf496a65bd846472048de2089", size = 4591122, upload-time = "2026-05-01T23:27:56.162Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/b9/11b341edf8d54e2694726b273fe9652b254d989f4f63e3ac6816ad6b55f4/psycopg_binary-3.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6402a9d8146cf4b3974ded3fd28a971e83dc6a0333eb7822524a3aa20b546578", size = 4669943, upload-time = "2026-05-01T23:28:04.522Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/18/4665bacd65e7865b4372fcd8abb8b9186ada4b0025f8c2ca691b364a556c/psycopg_binary-3.3.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:580ae30a5f95ccd90008ec697d3ed6a4a2047a516407ad904283fa42086936e9", size = 5469697, upload-time = "2026-05-01T23:28:11.337Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/b1/b83136c6e510593d9b0c759ba5384337bc4ad82d19fda675adc4b2703c84/psycopg_binary-3.3.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7510c37550f91a187e3660a8cc50d4b760f8c3b8b2f89ebc5698cd2c7f2c85d", size = 5152995, upload-time = "2026-05-01T23:28:20.529Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/8d/a9821e2a648afe6091989929982a3b0f00b2631a859cb81379728f08fb75/psycopg_binary-3.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77df19583501ea288eaf15ac0fe7ad01e6d8091a91d5c41df5c718f307d8e31b", size = 6738180, upload-time = "2026-05-01T23:28:30.654Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/58/2e349e8d23905dc2317b80ac65f48fb6f821a4777a4e994a60da91c4850f/psycopg_binary-3.3.4-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:018fbed325936da502feb546642c982dcc4b9ffdea32dfef78dbf3b7f7ad4070", size = 4978828, upload-time = "2026-05-01T23:28:37.277Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/48/57b00d03b4721878326122a1f1e6b0a90b85bcaec56b5b2f8ea6cfa45235/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17a21953a9e5ff3a16dab692625a3676e2f101db5e40072f39dbee2250194d68", size = 4509757, upload-time = "2026-05-01T23:28:43.078Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/37/33b47d8c007df69aec500df5889767c4d313748e8e9e27a2fef8a6dabcee/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:eb05ee1c2b817d27c537333224c9e83c7afb86fe7296ba970990068baf819b16", size = 4190546, upload-time = "2026-05-01T23:28:50.016Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/c6/32b0835dbc2122617902b649d76a91c1e75406e76bf3d595b0c3bb5ffad6/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:773d573e11f437ce0bdb95b7c18dc58390494f96d43f8b45b9760436114f7652", size = 3926197, upload-time = "2026-05-01T23:28:55.55Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/68/d190ef0c0c5b16ded07831dabc8ddd412f4cdab07ec6e30ed38d9bda0e1f/psycopg_binary-3.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e55ccbdfae79a2ed9c6369c3008a3025817ff9d7e27b32a2d84e2a4267e66e", size = 4236627, upload-time = "2026-05-01T23:29:05.336Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/8f/81dcbc2e8454b74d14881275ea45f00791052dac531a9fa8be1730d1685b/psycopg_binary-3.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:494ca54901be8cf9eb7e02c25b731f2317c378efa44f43e8f9bd0e1184ae7be4", size = 3560782, upload-time = "2026-05-01T23:29:11.967Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psycopg-pool"
|
||||||
|
version = "3.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/90/82/7a23d26039827ecd4ebe93905651029ddd307c5182ad59296dfb6f67b528/psycopg_pool-3.3.1.tar.gz", hash = "sha256:b10b10b7a175d5cc1592147dc5b7eec8a9e0834eb3ed2c4a92c858e2f51eb63c", size = 31661, upload-time = "2026-05-01T23:31:59.809Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/ed/89c2c620af0e1660354cd8aabf9f5b21f911597ce22acb37c805d6c86bc8/psycopg_pool-3.3.1-py3-none-any.whl", hash = "sha256:2af5b432941c4c9ad5c87b3fa410aec910ec8f7c122855897983a06c45f2e4b5", size = 40023, upload-time = "2026-05-01T23:31:53.136Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg2-binary"
|
name = "psycopg2-binary"
|
||||||
version = "2.9.12"
|
version = "2.9.12"
|
||||||
@@ -3738,11 +3817,15 @@ dependencies = [
|
|||||||
{ name = "fastapi", extra = ["standard"] },
|
{ name = "fastapi", extra = ["standard"] },
|
||||||
{ name = "image" },
|
{ name = "image" },
|
||||||
{ name = "langchain" },
|
{ name = "langchain" },
|
||||||
|
{ name = "langchain-anthropic" },
|
||||||
{ name = "langchain-community" },
|
{ name = "langchain-community" },
|
||||||
|
{ name = "langchain-core" },
|
||||||
|
{ name = "langchain-ollama" },
|
||||||
{ name = "langchain-openai" },
|
{ name = "langchain-openai" },
|
||||||
{ name = "langchain-qwq" },
|
{ name = "langchain-qwq" },
|
||||||
{ name = "langgraph" },
|
{ name = "langgraph" },
|
||||||
{ name = "langgraph-api" },
|
{ name = "langgraph-api" },
|
||||||
|
{ name = "langgraph-checkpoint-postgres" },
|
||||||
{ name = "langgraph-cli", extra = ["inmem"] },
|
{ name = "langgraph-cli", extra = ["inmem"] },
|
||||||
{ name = "langsmith" },
|
{ name = "langsmith" },
|
||||||
{ name = "load" },
|
{ name = "load" },
|
||||||
@@ -3759,6 +3842,7 @@ dependencies = [
|
|||||||
{ name = "pandas-stubs" },
|
{ name = "pandas-stubs" },
|
||||||
{ name = "pika" },
|
{ name = "pika" },
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
|
{ name = "psycopg", extra = ["binary", "pool"] },
|
||||||
{ name = "pyasyncore" },
|
{ name = "pyasyncore" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "pydantic-core" },
|
{ name = "pydantic-core" },
|
||||||
@@ -3797,11 +3881,15 @@ requires-dist = [
|
|||||||
{ name = "fastapi", extras = ["standard"], specifier = ">=0.125.0" },
|
{ name = "fastapi", extras = ["standard"], specifier = ">=0.125.0" },
|
||||||
{ name = "image", specifier = ">=1.5.33" },
|
{ name = "image", specifier = ">=1.5.33" },
|
||||||
{ name = "langchain", specifier = ">=1.2.0" },
|
{ name = "langchain", specifier = ">=1.2.0" },
|
||||||
|
{ name = "langchain-anthropic", specifier = ">=1.4.4" },
|
||||||
{ name = "langchain-community", specifier = ">=0.4.1" },
|
{ name = "langchain-community", specifier = ">=0.4.1" },
|
||||||
|
{ name = "langchain-core", specifier = ">=1.4.2" },
|
||||||
|
{ name = "langchain-ollama", specifier = ">=1.1.0" },
|
||||||
{ name = "langchain-openai", specifier = ">=1.2.2" },
|
{ name = "langchain-openai", specifier = ">=1.2.2" },
|
||||||
{ name = "langchain-qwq", specifier = ">=0.3.5" },
|
{ name = "langchain-qwq", specifier = ">=0.3.5" },
|
||||||
{ name = "langgraph", specifier = ">=1.0.5" },
|
{ name = "langgraph", specifier = ">=1.0.5" },
|
||||||
{ name = "langgraph-api", specifier = ">=0.4.28" },
|
{ name = "langgraph-api", specifier = ">=0.4.28" },
|
||||||
|
{ name = "langgraph-checkpoint-postgres", specifier = ">=3.1.0" },
|
||||||
{ name = "langgraph-cli", extras = ["inmem", "redis"], specifier = "<=0.4.26" },
|
{ name = "langgraph-cli", extras = ["inmem", "redis"], specifier = "<=0.4.26" },
|
||||||
{ name = "langsmith", specifier = ">=0.8.11" },
|
{ name = "langsmith", specifier = ">=0.8.11" },
|
||||||
{ name = "load", specifier = ">=1.0.14" },
|
{ name = "load", specifier = ">=1.0.14" },
|
||||||
@@ -3818,6 +3906,7 @@ requires-dist = [
|
|||||||
{ name = "pandas-stubs", specifier = "~=2.3.3" },
|
{ name = "pandas-stubs", specifier = "~=2.3.3" },
|
||||||
{ name = "pika", specifier = ">=1.3.2" },
|
{ name = "pika", specifier = ">=1.3.2" },
|
||||||
{ name = "pillow", specifier = ">=12.0.0" },
|
{ name = "pillow", specifier = ">=12.0.0" },
|
||||||
|
{ name = "psycopg", extras = ["binary", "pool"], specifier = ">=3.3.4" },
|
||||||
{ name = "pyasyncore", specifier = ">=1.0.4" },
|
{ name = "pyasyncore", specifier = ">=1.0.4" },
|
||||||
{ name = "pydantic", specifier = ">=2.12.5" },
|
{ name = "pydantic", specifier = ">=2.12.5" },
|
||||||
{ name = "pydantic-core", specifier = ">=2.41.5" },
|
{ name = "pydantic-core", specifier = ">=2.41.5" },
|
||||||
|
|||||||
Reference in New Issue
Block a user