aida agent (基础版)搭建完成
This commit is contained in:
47
app/api/api_fashion_agent.py
Normal file
47
app/api/api_fashion_agent.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
from app.schemas.fashion_agent import FashionAgentRequest
|
||||||
|
from app.service.fashion_agent.service import FashionAgentService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/agent/stream")
|
||||||
|
async def fashion_agent_stream(request_item: FashionAgentRequest):
|
||||||
|
"""
|
||||||
|
服装设计 Agent(流式输出)
|
||||||
|
|
||||||
|
- **message**: 用户输入的消息(必填)
|
||||||
|
- **user_id**: 用户ID,默认 "agent"
|
||||||
|
- **enable_thinking**: 是否开启思考模式
|
||||||
|
- **call_print**: 是否直接调用 print 生成印花
|
||||||
|
- **print_need_prompt_generation**: print 是否需要 LLM 生成 prompt
|
||||||
|
- **call_logo**: 是否直接调用 logo 生成装饰图案
|
||||||
|
- **call_sketch**: 是否直接调用 sketch 生成草图
|
||||||
|
- **sketch_need_prompt_generation**: sketch 是否需要 LLM 生成 prompt
|
||||||
|
- **call_design**: 是否直接调用 design 生成设计系列
|
||||||
|
- **design_request_data**: design 请求参数(objects, process_id, requestId, callback_url)
|
||||||
|
- **call_trending**: 是否直接调用 trending 趋势分析
|
||||||
|
- **call_explor**: 是否直接调用 explorer 灵感探索
|
||||||
|
- **provider**: 图片源 (pexels/unsplash),默认 unsplash
|
||||||
|
|
||||||
|
返回 SSE 事件流:
|
||||||
|
- **tools** 事件:工具调用的 started/finished 状态
|
||||||
|
- **custom** 事件:design 工具的逐个生成结果
|
||||||
|
- **Done** 事件:流结束标记
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info(f"fashion_agent stream request: {json.dumps(request_item.model_dump(), indent=4, ensure_ascii=False)}")
|
||||||
|
service = FashionAgentService()
|
||||||
|
return StreamingResponse(
|
||||||
|
service.run_stream(request_item),
|
||||||
|
media_type="text/event-stream",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"fashion_agent stream exception: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
@@ -4,6 +4,7 @@ from app.api import api_brand_dna
|
|||||||
from app.api import api_clothing_seg
|
from app.api import api_clothing_seg
|
||||||
from app.api import api_design
|
from app.api import api_design
|
||||||
from app.api import api_design_pre_processing
|
from app.api import api_design_pre_processing
|
||||||
|
from app.api import api_fashion_agent
|
||||||
from app.api import api_generate_image
|
from app.api import api_generate_image
|
||||||
from app.api import api_mannequins_edit
|
from app.api import api_mannequins_edit
|
||||||
from app.api import api_pose_transform
|
from app.api import api_pose_transform
|
||||||
@@ -28,6 +29,7 @@ router.include_router(api_mannequins_edit.router, tags=['api_mannequins_edit'],
|
|||||||
router.include_router(api_pose_transform.router, tags=['api_pose_transform'], prefix="/api")
|
router.include_router(api_pose_transform.router, tags=['api_pose_transform'], prefix="/api")
|
||||||
router.include_router(api_clothing_seg.router, tags=['api_clothing_seg'], prefix="/api")
|
router.include_router(api_clothing_seg.router, tags=['api_clothing_seg'], prefix="/api")
|
||||||
router.include_router(api_sketch_to_garment.router, tags=['sketch_to_garment'], prefix="/api")
|
router.include_router(api_sketch_to_garment.router, tags=['sketch_to_garment'], prefix="/api")
|
||||||
|
router.include_router(api_fashion_agent.router, tags=['fashion_agent'], prefix="/api")
|
||||||
|
|
||||||
"""停用"""
|
"""停用"""
|
||||||
# from app.api import api_chat_robot
|
# from app.api import api_chat_robot
|
||||||
|
|||||||
27
app/schemas/fashion_agent.py
Normal file
27
app/schemas/fashion_agent.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class FashionAgentRequest(BaseModel):
|
||||||
|
"""服装设计 Agent 请求"""
|
||||||
|
|
||||||
|
message: str = Field(default="", description="用户输入的消息")
|
||||||
|
user_id: str = Field(default="test-agent", description="用户ID,用于生成图片存储路径")
|
||||||
|
enable_thinking: bool = Field(default=False, description="模型思考是否开启")
|
||||||
|
|
||||||
|
call_print: bool = Field(default=False, description="是否直接调用 print 生成印花")
|
||||||
|
print_need_prompt_generation: bool = Field(default=False, description="print 是否需要 LLM 生成 prompt")
|
||||||
|
|
||||||
|
call_logo: bool = Field(default=False, description="是否直接调用 logo 生成装饰图案")
|
||||||
|
|
||||||
|
call_sketch: bool = Field(default=False, description="是否直接调用 sketch 生成草图")
|
||||||
|
sketch_need_prompt_generation: bool = Field(default=False, description="sketch 是否需要 LLM 生成 prompt")
|
||||||
|
|
||||||
|
call_design: bool = Field(default=False, description="是否直接调用 design 生成设计系列")
|
||||||
|
design_request_data: dict = Field(default={}, description="design 请求参数")
|
||||||
|
|
||||||
|
call_trending: bool = Field(default=False, description="是否直接调用 trending 趋势分析")
|
||||||
|
call_explor: bool = Field(default=False, description="是否直接调用 explorer 灵感探索")
|
||||||
|
|
||||||
|
provider: Optional[str] = Field(default="unsplash", description="图片源: pexels 或 unsplash")
|
||||||
@@ -294,12 +294,11 @@ def design_generate_v2(request_data):
|
|||||||
# 打印结果
|
# 打印结果
|
||||||
logger.info(response.text)
|
logger.info(response.text)
|
||||||
|
|
||||||
|
for step, object in enumerate(objects_data):
|
||||||
|
t = threading.Thread(target=process_object, args=(object, callback_url))
|
||||||
|
threads.append(t)
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
# 等待所有线程完成,释放局部变量(图片等大对象)
|
|
||||||
for t in threads:
|
|
||||||
t.join()
|
|
||||||
|
|
||||||
|
|
||||||
def post_request(url, data=None, json_data=None, headers=None, auth=None, timeout=5):
|
def post_request(url, data=None, json_data=None, headers=None, auth=None, timeout=5):
|
||||||
"""
|
"""
|
||||||
@@ -320,611 +319,3 @@ def post_request(url, data=None, json_data=None, headers=None, auth=None, timeou
|
|||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
print(f"POST请求出错: {e}")
|
print(f"POST请求出错: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
object_data = {
|
|
||||||
"objects": [
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 98419,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/dress/0825000526.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Dress",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 98420,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/skirt/903000127.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Skirt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 69140,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/blouse/0902001100.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Blouse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 81604,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/outwear/outwear_p5_729.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Outwear",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 63964,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/outwear/0825001572.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Outwear",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 98421,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/blouse/blouse_506.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Blouse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 98422,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/trousers/0628001244.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Trousers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 79927,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/outwear/0825000378.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Outwear",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 67473,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/blouse/0825001350.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Blouse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 80046,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/skirt/0628001443.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Skirt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 84148,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/trousers/0628000751.jpeg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Trousers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 97321,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/blouse/0902000222.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Blouse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 90718,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/outwear/0825000314.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Outwear",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 86403,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/skirt/0902000231.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Skirt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 87135,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/blouse/0902001315.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Blouse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 87428,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/outwear/0902000566.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Outwear",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 98423,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/dress/0916001596.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Dress",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basic": {
|
|
||||||
"body_point_test": {
|
|
||||||
"waistband_right": [203, 249],
|
|
||||||
"hand_point_right": [229, 343],
|
|
||||||
"waistband_left": [119, 248],
|
|
||||||
"hand_point_left": [97, 343],
|
|
||||||
"shoulder_left": [108, 107],
|
|
||||||
"shoulder_right": [212, 107],
|
|
||||||
},
|
|
||||||
"layer_order": False,
|
|
||||||
"scale_bag": 0.7,
|
|
||||||
"scale_earrings": 0.16,
|
|
||||||
"self_template": True,
|
|
||||||
"single_overall": "overall",
|
|
||||||
"switch_category": "",
|
|
||||||
},
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 86345,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/outwear/0825000695.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Outwear",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 78743,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/blouse/0902001412.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Blouse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "28 26 26",
|
|
||||||
"icon": "none",
|
|
||||||
"image_id": 68988,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"path": "aida-sys-image/images/female/trousers/0825000403.jpg",
|
|
||||||
"print": {
|
|
||||||
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
|
||||||
"overall": {
|
|
||||||
"location": [[0.0, 0.0]],
|
|
||||||
"print_angle_list": [0.0, 0.0],
|
|
||||||
"print_path_list": [],
|
|
||||||
"print_scale_list": [0.0, 0.0],
|
|
||||||
},
|
|
||||||
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
|
||||||
},
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Trousers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
|
||||||
"image_id": 67277,
|
|
||||||
"offset": [1, 1],
|
|
||||||
"resize_scale": [1.0, 1.0],
|
|
||||||
"type": "Body",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"process_id": "123",
|
|
||||||
}
|
|
||||||
start_time = time.time()
|
|
||||||
X = design_generate(object_data)
|
|
||||||
print(time.time() - start_time)
|
|
||||||
print(X)
|
|
||||||
|
|||||||
0
app/service/fashion_agent/__init__.py
Normal file
0
app/service/fashion_agent/__init__.py
Normal file
159
app/service/fashion_agent/graph_node/design_graph/graph.py
Normal file
159
app/service/fashion_agent/graph_node/design_graph/graph.py
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
|
||||||
|
from langchain_core.messages import AIMessage, AnyMessage
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
|
||||||
|
from app.service.fashion_agent.graph_node.design_graph.tools import design_tool # noqa: E402
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
|
class DesignState(TypedDict):
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
|
||||||
|
design_request_data: dict = {}
|
||||||
|
request_objects: list[dict] = []
|
||||||
|
results: list[dict] = []
|
||||||
|
|
||||||
|
|
||||||
|
"""节点"""
|
||||||
|
|
||||||
|
|
||||||
|
def run_design_node(state: DesignState) -> dict:
|
||||||
|
"""调用 design_tool 执行设计任务,逐个推送结果"""
|
||||||
|
from langgraph.config import get_stream_writer
|
||||||
|
|
||||||
|
writer = get_stream_writer()
|
||||||
|
|
||||||
|
request_data = state.get("design_request_data")
|
||||||
|
request_objects = request_data.get("objects")
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for item in design_tool.invoke({"objects": request_objects}):
|
||||||
|
logger.info(f"design result: {item}")
|
||||||
|
results.append(item)
|
||||||
|
writer({"event": "tool-output-delta", "tool_name": "design_tool", "type": "design_result", "data": item})
|
||||||
|
|
||||||
|
writer({"event": "tool-finished", "tool_name": "design_tool", "type": "design_result", "data": results})
|
||||||
|
result_text = f"设计完成,共处理 {len(results)} 个对象"
|
||||||
|
return {"results": results, "messages": [AIMessage(content=result_text)]}
|
||||||
|
|
||||||
|
|
||||||
|
"""构建 Graph"""
|
||||||
|
|
||||||
|
|
||||||
|
def build_design_graph():
|
||||||
|
"""构建 design graph"""
|
||||||
|
workflow = StateGraph(DesignState)
|
||||||
|
|
||||||
|
workflow.add_node("run_design", run_design_node)
|
||||||
|
|
||||||
|
workflow.add_edge(START, "run_design")
|
||||||
|
workflow.add_edge("run_design", END)
|
||||||
|
|
||||||
|
return workflow.compile()
|
||||||
|
|
||||||
|
|
||||||
|
design_graph = build_design_graph()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
request_data = {
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"basic": {
|
||||||
|
"body_point_test": {
|
||||||
|
"waistband_right": [203, 249],
|
||||||
|
"hand_point_right": [229, 343],
|
||||||
|
"waistband_left": [119, 248],
|
||||||
|
"hand_point_left": [97, 343],
|
||||||
|
"shoulder_left": [108, 107],
|
||||||
|
"relation_type": "System",
|
||||||
|
"shoulder_right": [212, 107],
|
||||||
|
"relation_id": 1020356,
|
||||||
|
},
|
||||||
|
"layer_order": False,
|
||||||
|
"scale_bag": 0.7,
|
||||||
|
"scale_earrings": 0.16,
|
||||||
|
"self_template": False,
|
||||||
|
"single_overall": "overall",
|
||||||
|
"switch_category": "",
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"color": "209 196 171",
|
||||||
|
"image_id": 84093,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"path": "aida-users/89/sketchboard/female/Outwear/0943d209-7ce0-408c-bc61-83f15da94138.png",
|
||||||
|
"print": {
|
||||||
|
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
||||||
|
"overall": {
|
||||||
|
"location": [[0.0, 0.0]],
|
||||||
|
"print_angle_list": [0.0, 0.0],
|
||||||
|
"print_path_list": [],
|
||||||
|
"print_scale_list": [[0.0, 0.0]],
|
||||||
|
},
|
||||||
|
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
||||||
|
},
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Outwear",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "63 71 73",
|
||||||
|
"image_id": 100496,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"path": "aida-sys-image/images/female/blouse/0628001684.jpg",
|
||||||
|
"print": {
|
||||||
|
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
||||||
|
"overall": {
|
||||||
|
"location": [[0.0, 0.0]],
|
||||||
|
"print_angle_list": [0.0, 0.0],
|
||||||
|
"print_path_list": [],
|
||||||
|
"print_scale_list": [[0.0, 0.0]],
|
||||||
|
},
|
||||||
|
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
||||||
|
},
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Blouse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "111 78 63",
|
||||||
|
"gradient": "aida-gradient/f69b98e8-4248-4f7a-98a2-21bac41bf3e0.png",
|
||||||
|
"image_id": 92193,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"path": "aida-sys-image/images/female/trousers/0825001160.jpg",
|
||||||
|
"print": {
|
||||||
|
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
||||||
|
"overall": {
|
||||||
|
"location": [[0.0, 0.0]],
|
||||||
|
"print_angle_list": [0.0, 0.0],
|
||||||
|
"print_path_list": [],
|
||||||
|
"print_scale_list": [[0.0, 0.0]],
|
||||||
|
},
|
||||||
|
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
||||||
|
},
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Trousers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
||||||
|
"image_id": 67277,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Body",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"objectSign": "65830966",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"process_id": "4802946666428422",
|
||||||
|
"requestId": "1d1e7641-0d62-4da2-adc0-b4404910723c",
|
||||||
|
"callback_url": "https://api.aida.com.hk/api/third/party/receiveDesignResults",
|
||||||
|
}
|
||||||
|
result = design_graph.invoke({"design_request_data": request_data})
|
||||||
|
print(result)
|
||||||
206
app/service/fashion_agent/graph_node/design_graph/tools.py
Normal file
206
app/service/fashion_agent/graph_node/design_graph/tools.py
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import logging
|
||||||
|
import queue
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from langchain.tools import tool
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from app.service.design_fast.design_generate import process_item, process_layer
|
||||||
|
from app.service.design_fast.utils.synthesis_item import synthesis, synthesis_single, update_base_size_priority
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class DesignModel(BaseModel):
|
||||||
|
objects: list[dict] = Field(description="")
|
||||||
|
|
||||||
|
|
||||||
|
@tool(args_schema=DesignModel, description="design tool")
|
||||||
|
def design_tool(objects: list[dict]):
|
||||||
|
"""design tool"""
|
||||||
|
|
||||||
|
result_queue = queue.Queue()
|
||||||
|
|
||||||
|
def process_object(obj):
|
||||||
|
basic = obj["basic"]
|
||||||
|
design_type = basic.get("design_type", "default")
|
||||||
|
items_response = {
|
||||||
|
"layers": [],
|
||||||
|
"objectSign": obj["objectSign"] if "objectSign" in obj.keys() else "",
|
||||||
|
}
|
||||||
|
if basic["single_overall"] == "overall":
|
||||||
|
item_results = []
|
||||||
|
for item in obj["items"]:
|
||||||
|
item_results.append(process_item(item, basic, design_type))
|
||||||
|
layers = []
|
||||||
|
for item in item_results:
|
||||||
|
process_layer(item, layers)
|
||||||
|
layers = sorted(layers, key=lambda s: s.get("priority", float("inf")))
|
||||||
|
|
||||||
|
layers, new_size = update_base_size_priority(layers)
|
||||||
|
|
||||||
|
for lay in layers:
|
||||||
|
items_response["layers"].append(
|
||||||
|
{
|
||||||
|
"image_category": "body" if lay["name"] == "mannequin" else lay["name"],
|
||||||
|
"position": lay["position"],
|
||||||
|
"priority": lay.get("priority", None),
|
||||||
|
"resize_scale": lay["resize_scale"] if "resize_scale" in lay.keys() else None,
|
||||||
|
"image_size": lay["image"] if lay["image"] is None else lay["image"].size,
|
||||||
|
"gradient_string": lay["gradient_string"] if "gradient_string" in lay.keys() else "",
|
||||||
|
"mask_url": lay["mask_url"],
|
||||||
|
"image_url": lay["image_url"] if "image_url" in lay.keys() else None,
|
||||||
|
"pattern_overall_image_url": (
|
||||||
|
lay["pattern_overall_image_url"] if "pattern_overall_image_url" in lay.keys() else None
|
||||||
|
),
|
||||||
|
"pattern_print_image_url": lay["pattern_print_image_url"] if "pattern_print_image_url" in lay.keys() else None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
items_response["synthesis_url"] = synthesis(layers, new_size, basic)
|
||||||
|
else:
|
||||||
|
item_result = process_item(obj["items"][0], basic, design_type)
|
||||||
|
items_response["layers"].append(
|
||||||
|
{
|
||||||
|
"image_category": f"{item_result['name']}_front",
|
||||||
|
"image_size": item_result["back_image"].size if item_result["back_image"] else None,
|
||||||
|
"position": None,
|
||||||
|
"priority": 0,
|
||||||
|
"image_url": item_result["front_image_url"],
|
||||||
|
"mask_url": item_result["mask_url"],
|
||||||
|
"gradient_string": item_result["gradient_string"] if "gradient_string" in item_result.keys() else "",
|
||||||
|
"pattern_overall_image_url": (
|
||||||
|
item_result["pattern_overall_image_url"] if "pattern_overall_image_url" in item_result.keys() else None
|
||||||
|
),
|
||||||
|
"pattern_print_image_url": (
|
||||||
|
item_result["pattern_print_image_url"] if "pattern_print_image_url" in item_result.keys() else None
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
items_response["layers"].append(
|
||||||
|
{
|
||||||
|
"image_category": f"{item_result['name']}_back",
|
||||||
|
"image_size": item_result["front_image"].size if item_result["front_image"] else None,
|
||||||
|
"position": None,
|
||||||
|
"priority": 0,
|
||||||
|
"image_url": item_result["back_image_url"],
|
||||||
|
"mask_url": item_result["mask_url"],
|
||||||
|
"gradient_string": item_result["gradient_string"] if "gradient_string" in item_result.keys() else "",
|
||||||
|
"pattern_overall_image_url": (
|
||||||
|
item_result["pattern_overall_image_url"] if "pattern_overall_image_url" in item_result.keys() else None
|
||||||
|
),
|
||||||
|
"pattern_print_image_url": (
|
||||||
|
item_result["pattern_print_image_url"] if "pattern_print_image_url" in item_result.keys() else None
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
items_response["synthesis_url"] = synthesis_single(item_result["front_image"], item_result["back_image"])
|
||||||
|
logger.info(items_response)
|
||||||
|
result_queue.put(items_response)
|
||||||
|
|
||||||
|
# 启动所有线程
|
||||||
|
threads = []
|
||||||
|
for obj in objects:
|
||||||
|
t = threading.Thread(target=process_object, args=(obj,))
|
||||||
|
threads.append(t)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
# 主线程逐个取出结果 yield
|
||||||
|
finished = 0
|
||||||
|
total = len(objects)
|
||||||
|
while finished < total:
|
||||||
|
result = result_queue.get()
|
||||||
|
yield result
|
||||||
|
finished += 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
request_objects = [
|
||||||
|
{
|
||||||
|
"basic": {
|
||||||
|
"body_point_test": {
|
||||||
|
"waistband_right": [203, 249],
|
||||||
|
"hand_point_right": [229, 343],
|
||||||
|
"waistband_left": [119, 248],
|
||||||
|
"hand_point_left": [97, 343],
|
||||||
|
"shoulder_left": [108, 107],
|
||||||
|
"relation_type": "System",
|
||||||
|
"shoulder_right": [212, 107],
|
||||||
|
"relation_id": 1020356,
|
||||||
|
},
|
||||||
|
"layer_order": False,
|
||||||
|
"scale_bag": 0.7,
|
||||||
|
"scale_earrings": 0.16,
|
||||||
|
"self_template": False,
|
||||||
|
"single_overall": "overall",
|
||||||
|
"switch_category": "",
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"color": "209 196 171",
|
||||||
|
"image_id": 84093,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"path": "aida-users/89/sketchboard/female/Outwear/0943d209-7ce0-408c-bc61-83f15da94138.png",
|
||||||
|
"print": {
|
||||||
|
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
||||||
|
"overall": {
|
||||||
|
"location": [[0.0, 0.0]],
|
||||||
|
"print_angle_list": [0.0, 0.0],
|
||||||
|
"print_path_list": [],
|
||||||
|
"print_scale_list": [[0.0, 0.0]],
|
||||||
|
},
|
||||||
|
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
||||||
|
},
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Outwear",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "63 71 73",
|
||||||
|
"image_id": 100496,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"path": "aida-sys-image/images/female/blouse/0628001684.jpg",
|
||||||
|
"print": {
|
||||||
|
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
||||||
|
"overall": {
|
||||||
|
"location": [[0.0, 0.0]],
|
||||||
|
"print_angle_list": [0.0, 0.0],
|
||||||
|
"print_path_list": [],
|
||||||
|
"print_scale_list": [[0.0, 0.0]],
|
||||||
|
},
|
||||||
|
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
||||||
|
},
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Blouse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "111 78 63",
|
||||||
|
"gradient": "aida-gradient/f69b98e8-4248-4f7a-98a2-21bac41bf3e0.png",
|
||||||
|
"image_id": 92193,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"path": "aida-sys-image/images/female/trousers/0825001160.jpg",
|
||||||
|
"print": {
|
||||||
|
"element": {"element_angle_list": [], "element_path_list": [], "element_scale_list": [], "location": []},
|
||||||
|
"overall": {
|
||||||
|
"location": [[0.0, 0.0]],
|
||||||
|
"print_angle_list": [0.0, 0.0],
|
||||||
|
"print_path_list": [],
|
||||||
|
"print_scale_list": [[0.0, 0.0]],
|
||||||
|
},
|
||||||
|
"single": {"location": [], "print_angle_list": [], "print_path_list": [], "print_scale_list": []},
|
||||||
|
},
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Trousers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"body_path": "aida-sys-image/models/female/2e4815b9-1191-419d-94ed-5771239ca4a5.png",
|
||||||
|
"image_id": 67277,
|
||||||
|
"offset": [1, 1],
|
||||||
|
"resize_scale": [1.0, 1.0],
|
||||||
|
"type": "Body",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"objectSign": "65830966",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
result = design_tool.invoke({"objects": request_objects})
|
||||||
|
for item in result:
|
||||||
|
print(item)
|
||||||
138
app/service/fashion_agent/graph_node/explorer_graph/graph.py
Normal file
138
app/service/fashion_agent/graph_node/explorer_graph/graph.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
|
||||||
|
from langchain.tools import ToolRuntime
|
||||||
|
from langchain_core.messages import AIMessage, AnyMessage, HumanMessage, SystemMessage
|
||||||
|
from langchain_qwq import ChatQwen
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from langchain_core.runnables import RunnableConfig
|
||||||
|
from app.service.fashion_agent.graph_node.explorer_graph.tools import explor_tool
|
||||||
|
from app.service.fashion_agent.init_llm import build_llm
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
|
class ExplorerState(TypedDict):
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
input_text: str
|
||||||
|
search_query: str
|
||||||
|
image_results: list[dict] # 每项包含 image_url 和 minio_path
|
||||||
|
provider: str = "unsplash" # 图片源: "pexels" 或 "unsplash"
|
||||||
|
|
||||||
|
|
||||||
|
"""节点"""
|
||||||
|
|
||||||
|
|
||||||
|
def extract_input_node(state: ExplorerState) -> dict:
|
||||||
|
"""从 messages 中提取用户输入"""
|
||||||
|
input_text = state["messages"][0].content if state.get("messages") else ""
|
||||||
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
|
class SearchQuery(BaseModel):
|
||||||
|
"""搜索关键词"""
|
||||||
|
|
||||||
|
query: str = Field(description="用于搜索灵感图片的英文关键词,简洁有力")
|
||||||
|
|
||||||
|
|
||||||
|
# TODO 要考虑搜索图片失败或者图片不存在的情况, 搜索不到 需要调整搜索词或者拆分搜索词,最终失败的话调用mood board生成工具生成, 保证绝对有图片
|
||||||
|
async def generate_query_node(state: ExplorerState) -> dict:
|
||||||
|
"""使用 LLM 分析用户输入,生成搜索关键词"""
|
||||||
|
input_text = state["input_text"]
|
||||||
|
logger.info(f"[Explorer] 用户输入: {input_text}")
|
||||||
|
llm = build_llm()
|
||||||
|
|
||||||
|
structured_llm = llm.with_structured_output(SearchQuery)
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
SystemMessage(content="""你是一个专业的服装设计师助手。
|
||||||
|
根据用户输入,生成一个英文搜索关键词,用于在图片库中搜索服装设计灵感图片(moodboard)。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 使用英文,简洁有力
|
||||||
|
2. 适合搜索高质量的设计灵感图片
|
||||||
|
|
||||||
|
例如:
|
||||||
|
用户输入:"夏季连衣裙,清新风格"
|
||||||
|
输出:summer dress fresh style"""),
|
||||||
|
HumanMessage(content=input_text),
|
||||||
|
]
|
||||||
|
|
||||||
|
result = structured_llm.invoke(messages)
|
||||||
|
logger.info(f"[Explorer] LLM 生成的搜索关键词: {result.query}")
|
||||||
|
return {"search_query": result.query}
|
||||||
|
|
||||||
|
|
||||||
|
async def search_and_upload_node(state: ExplorerState, config: RunnableConfig) -> dict:
|
||||||
|
"""使用搜索关键词获取图片并上传到 minio"""
|
||||||
|
query = state.get("search_query", "")
|
||||||
|
user_id = state.get("user_id", "agent")
|
||||||
|
provider = state.get("provider", "unsplash")
|
||||||
|
|
||||||
|
try:
|
||||||
|
results = await explor_tool.ainvoke({"query": query, "per_page": 4, "user_id": user_id, "method": provider}, config=config)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[Explorer] 搜索失败 '{query}': {e}")
|
||||||
|
results = []
|
||||||
|
|
||||||
|
return {"image_results": results}
|
||||||
|
|
||||||
|
|
||||||
|
def summarize_node(state: ExplorerState) -> dict:
|
||||||
|
"""汇总结果"""
|
||||||
|
input_text = state.get("input_text", "")
|
||||||
|
query = state.get("search_query", "")
|
||||||
|
results = state.get("image_results", [])
|
||||||
|
|
||||||
|
result_text = f"【灵感探索 Moodboard】\n\n"
|
||||||
|
result_text += f"基于您的需求:「{input_text}」\n"
|
||||||
|
result_text += f"搜索关键词:{query}\n\n"
|
||||||
|
result_text += f"已为您找到 {len(results)} 张灵感图片:\n"
|
||||||
|
|
||||||
|
for i, item in enumerate(results, 1):
|
||||||
|
result_text += f" {i}. 原图: {item.get('image_url', '')}\n"
|
||||||
|
result_text += f" Minio: {item.get('minio_path', '')}\n"
|
||||||
|
|
||||||
|
return {"messages": [AIMessage(content=result_text)]}
|
||||||
|
|
||||||
|
|
||||||
|
"""构建图"""
|
||||||
|
|
||||||
|
|
||||||
|
def build_explorer_graph():
|
||||||
|
"""构建灵感探索图"""
|
||||||
|
workflow = StateGraph(ExplorerState)
|
||||||
|
|
||||||
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
|
workflow.add_node("generate_query", generate_query_node)
|
||||||
|
workflow.add_node("search_and_upload", search_and_upload_node)
|
||||||
|
workflow.add_node("summarize", summarize_node)
|
||||||
|
|
||||||
|
workflow.add_edge(START, "extract_input")
|
||||||
|
workflow.add_edge("extract_input", "generate_query")
|
||||||
|
workflow.add_edge("generate_query", "search_and_upload")
|
||||||
|
workflow.add_edge("search_and_upload", "summarize")
|
||||||
|
workflow.add_edge("summarize", END)
|
||||||
|
|
||||||
|
return workflow.compile()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
graph = build_explorer_graph()
|
||||||
|
result = await graph.ainvoke(
|
||||||
|
{
|
||||||
|
"messages": [HumanMessage(content="夏季连衣裙,清新自然风格")],
|
||||||
|
"provider": "unsplash",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(result["messages"][-1].content)
|
||||||
|
|
||||||
|
asyncio.run(test())
|
||||||
54
app/service/fashion_agent/graph_node/explorer_graph/tools.py
Normal file
54
app/service/fashion_agent/graph_node/explorer_graph/tools.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from langchain.tools import ToolRuntime, tool
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from langchain_core.runnables import RunnableConfig
|
||||||
|
from app.service.fashion_agent.graph_node.node_tools.pexels_search import search_photos
|
||||||
|
from app.service.fashion_agent.graph_node.node_tools.unsplash_search import get_random_photos
|
||||||
|
|
||||||
|
|
||||||
|
class SearchInput(BaseModel):
|
||||||
|
"""Input schema for Pexels Search Tool."""
|
||||||
|
|
||||||
|
query: str = Field(description="Search query for Pexels, e.g., 'minimalist fashion moodboard', 'summer dress inspiration'")
|
||||||
|
per_page: int = Field(description="Number of images to return (1-80)", default=4)
|
||||||
|
user_id: str = Field(description="User ID for image storage", default="agent")
|
||||||
|
method: str = Field(description="", default="unsplash")
|
||||||
|
|
||||||
|
|
||||||
|
@tool(args_schema=SearchInput)
|
||||||
|
async def explor_tool(
|
||||||
|
query: str, per_page: int = 4, user_id: str = "agent", method: str = "unsplash", config: RunnableConfig = None
|
||||||
|
) -> list[dict]:
|
||||||
|
"""Search for fashion inspiration images on Unsplash and upload to minio. Returns a list of dicts with image_url and minio_path."""
|
||||||
|
if config:
|
||||||
|
# 方式 1:从 configurable 获取
|
||||||
|
user_id = config.get("configurable", {}).get("user_id", "agent")
|
||||||
|
|
||||||
|
if method == "unsplash":
|
||||||
|
return await get_random_photos(query, count=per_page, user_id=user_id)
|
||||||
|
elif method == "pexels":
|
||||||
|
return await search_photos(query, per_page=per_page, user_id=user_id)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
urls = await get_random_photos("summer dress fresh natural style", count=4)
|
||||||
|
print(f"Uploaded {len(urls)} images to minio:")
|
||||||
|
for url in urls:
|
||||||
|
print(f" {url}")
|
||||||
|
|
||||||
|
asyncio.run(test())
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
urls = await search_photos("minimalist fashion moodboard", per_page=4)
|
||||||
|
print(f"Uploaded {len(urls)} images to minio:")
|
||||||
|
for url in urls:
|
||||||
|
print(f" {url}")
|
||||||
|
|
||||||
|
asyncio.run(test())
|
||||||
152
app/service/fashion_agent/graph_node/logo_graph/graph.py
Normal file
152
app/service/fashion_agent/graph_node/logo_graph/graph.py
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import asyncio
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
from langchain_core.messages import AIMessage, AnyMessage
|
||||||
|
from langchain_qwq import ChatQwen
|
||||||
|
from langchain_core.messages import HumanMessage, SystemMessage
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from app.service.fashion_agent.graph_node.node_tools.generate_logo import generate_logo_tool
|
||||||
|
from app.service.fashion_agent.init_llm import qwen_plus_llm
|
||||||
|
|
||||||
|
"""初始化 LLM TODO 将 API Key 替换为环境变量或者配置文件中的值,避免在代码中硬编码敏感信息"""
|
||||||
|
|
||||||
|
|
||||||
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
|
class LogoState(TypedDict):
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
input_text: str
|
||||||
|
user_id: str = "agent"
|
||||||
|
role: str = ""
|
||||||
|
gender: str = ""
|
||||||
|
style: str = ""
|
||||||
|
need_prompt_generation: bool = True # 是否需要使用 prompt 生成节点
|
||||||
|
|
||||||
|
logo_num: int = 1
|
||||||
|
|
||||||
|
logo_prompts: list[str] = []
|
||||||
|
logo_img_urls: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
"""生成 Logo 的提示词节点"""
|
||||||
|
|
||||||
|
|
||||||
|
# 定义输出结构
|
||||||
|
class LogoPrompt(BaseModel):
|
||||||
|
"""生成的 Logo 图像提示词"""
|
||||||
|
|
||||||
|
prompts: list[str] = Field(description="用于生成 Logo 的详细提示词")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_input_node(state: LogoState) -> dict:
|
||||||
|
"""从 messages 中提取用户输入"""
|
||||||
|
input_text = state["messages"][0].content if state.get("messages") else ""
|
||||||
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_logo_prompt_node(state: LogoState) -> dict:
|
||||||
|
"""根据用户输入生成 Logo 的图像生成提示词"""
|
||||||
|
structured_llm = qwen_plus_llm.with_structured_output(LogoPrompt)
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
SystemMessage(content="""从用户输入中提取核心主题词,只输出一个简单的英文单词。
|
||||||
|
例如:
|
||||||
|
- "我想要一个猫咪图案" -> "cat"
|
||||||
|
- "设计一个花朵" -> "flower"
|
||||||
|
- "可爱的狗" -> "dog"
|
||||||
|
只输出单词,不要其他内容。"""),
|
||||||
|
HumanMessage(content=state["input_text"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
result = structured_llm.invoke(messages)
|
||||||
|
prompts = result.prompts
|
||||||
|
|
||||||
|
return {
|
||||||
|
"logo_prompts": prompts,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"""生成 Logo 图案节点"""
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_logo_img_node(state: LogoState) -> dict:
|
||||||
|
"""根据生成的提示词,生成 Logo 图案"""
|
||||||
|
# 如果 logo_prompts 为空,使用 input_text 作为 prompt
|
||||||
|
prompts = state["logo_prompts"] if state["logo_prompts"] else [state["input_text"]]
|
||||||
|
|
||||||
|
logo_img_urls = []
|
||||||
|
for i in range(state.get("logo_num", 1)):
|
||||||
|
image_url = await generate_logo_tool.ainvoke({"prompt": prompts[i], "user_id": state.get("user_id", "agent")})
|
||||||
|
logo_img_urls.append(image_url)
|
||||||
|
|
||||||
|
result_text = f"Logo 生成完成,共生成 {len(logo_img_urls)} 张图片:\n"
|
||||||
|
return {"logo_img_urls": logo_img_urls, "messages": [AIMessage(content=result_text)]}
|
||||||
|
|
||||||
|
|
||||||
|
"""条件分支 判断是否需要生成 prompt"""
|
||||||
|
|
||||||
|
|
||||||
|
def should_generate_prompt(state: LogoState) -> str:
|
||||||
|
"""条件分支:判断是否需要生成 prompt"""
|
||||||
|
if state.get("need_prompt_generation", True):
|
||||||
|
return "gen_prompt"
|
||||||
|
else:
|
||||||
|
return "gen_logo"
|
||||||
|
|
||||||
|
|
||||||
|
def build_logo_graph():
|
||||||
|
"""构建独立的画像收集 Graph"""
|
||||||
|
|
||||||
|
workflow = StateGraph(LogoState)
|
||||||
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
|
workflow.add_node("gen_prompt", generate_logo_prompt_node)
|
||||||
|
workflow.add_node("gen_logo", generate_logo_img_node)
|
||||||
|
|
||||||
|
# 添加边
|
||||||
|
workflow.add_edge(START, "extract_input")
|
||||||
|
workflow.add_conditional_edges(
|
||||||
|
"extract_input",
|
||||||
|
should_generate_prompt,
|
||||||
|
{
|
||||||
|
"gen_prompt": "gen_prompt",
|
||||||
|
"gen_logo": "gen_logo",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
workflow.add_edge("gen_prompt", "gen_logo")
|
||||||
|
workflow.add_edge("gen_logo", END)
|
||||||
|
|
||||||
|
graph = workflow.compile()
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
async def main(test_input, user_id="agent", need_prompt_generation=True):
|
||||||
|
graph = build_logo_graph()
|
||||||
|
result = await graph.ainvoke(
|
||||||
|
{
|
||||||
|
"input_text": test_input,
|
||||||
|
"user_id": user_id,
|
||||||
|
"logo_prompts": [] if need_prompt_generation else [test_input],
|
||||||
|
"need_prompt_generation": need_prompt_generation,
|
||||||
|
"role": "",
|
||||||
|
"gender": "",
|
||||||
|
"style": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
user_prompt = "golden retriever"
|
||||||
|
result = asyncio.run(main(user_prompt, need_prompt_generation=False))
|
||||||
|
print("\n=== 直接使用 prompt ===")
|
||||||
|
print(f"Result: {result}")
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import httpx
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_image(
|
||||||
|
bucket_name="fida-public-bucket",
|
||||||
|
object_name=f"furniture/sketches/123456.png",
|
||||||
|
prompt="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.",
|
||||||
|
):
|
||||||
|
request_data = {
|
||||||
|
"input_image_paths": [],
|
||||||
|
"prompt": prompt,
|
||||||
|
"bucket_name": bucket_name,
|
||||||
|
"object_name": object_name,
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
}
|
||||||
|
async with httpx.AsyncClient(timeout=120) as client:
|
||||||
|
resp = await client.post(
|
||||||
|
f"http://20.1.1.33:14202/predict",
|
||||||
|
json=request_data,
|
||||||
|
)
|
||||||
|
result = resp.json()
|
||||||
|
image_url = result.get("output_path", None)
|
||||||
|
return image_url
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import asyncio
|
||||||
|
import concurrent.futures
|
||||||
|
import random
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import tritonclient.grpc as grpcclient
|
||||||
|
from langchain.tools import tool
|
||||||
|
from PIL import Image
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from tritonclient.utils import np_to_triton_dtype
|
||||||
|
from uuid_utils import uuid7
|
||||||
|
from app.core.config import settings
|
||||||
|
from app.service.generate_image.utils.upload_sd_image import upload_SDXL_image
|
||||||
|
|
||||||
|
# 模型配置
|
||||||
|
GSL_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:10041"
|
||||||
|
GSL_MODEL_NAME = "stable_diffusion_xl_transparent"
|
||||||
|
|
||||||
|
# 线程池用于执行同步推理
|
||||||
|
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_logo_sync(prompt: str) -> Image.Image:
|
||||||
|
"""同步生成 Logo 的内部函数"""
|
||||||
|
seed = random.randint(0, 2**32 - 1)
|
||||||
|
grpc_client = grpcclient.InferenceServerClient(url=GSL_MODEL_URL)
|
||||||
|
|
||||||
|
# 准备输入
|
||||||
|
prompts = [prompt]
|
||||||
|
text_obj = np.array(prompts, dtype="object").reshape((-1, 1))
|
||||||
|
input_text = grpcclient.InferInput("prompt", text_obj.shape, np_to_triton_dtype(text_obj.dtype))
|
||||||
|
input_text.set_data_from_numpy(text_obj)
|
||||||
|
|
||||||
|
negative_prompts = "bad, ugly"
|
||||||
|
text_obj_neg = np.array(negative_prompts, dtype="object").reshape((-1, 1))
|
||||||
|
input_text_neg = grpcclient.InferInput("negative_prompt", text_obj_neg.shape, np_to_triton_dtype(text_obj_neg.dtype))
|
||||||
|
input_text_neg.set_data_from_numpy(text_obj_neg)
|
||||||
|
|
||||||
|
seed_input = np.array(seed, dtype="object").reshape((-1, 1))
|
||||||
|
input_seed = grpcclient.InferInput("seed", seed_input.shape, np_to_triton_dtype(seed_input.dtype))
|
||||||
|
input_seed.set_data_from_numpy(seed_input)
|
||||||
|
|
||||||
|
inputs = [input_text, input_text_neg, input_seed]
|
||||||
|
|
||||||
|
# 同步推理
|
||||||
|
result = grpc_client.infer(model_name=GSL_MODEL_NAME, inputs=inputs)
|
||||||
|
image = result.as_numpy("generated_image")
|
||||||
|
return Image.fromarray(np.squeeze(image.astype(np.uint8)))
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_logo(prompt: str) -> Image.Image:
|
||||||
|
"""异步生成透明背景的 Logo 图片"""
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(executor, _generate_logo_sync, prompt)
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateLogoToolInput(BaseModel):
|
||||||
|
"""Input schema for the Generate Logo Tool."""
|
||||||
|
|
||||||
|
prompt: str = Field(description="Simple keyword for logo generation, e.g., 'cat', 'flower', 'dog'")
|
||||||
|
user_id: str = Field(description="User ID for image storage", default="agent")
|
||||||
|
|
||||||
|
|
||||||
|
@tool(args_schema=GenerateLogoToolInput)
|
||||||
|
async def generate_logo_tool(prompt: str, user_id: str = "agent") -> str:
|
||||||
|
"""Generate a transparent background logo image based on a simple keyword."""
|
||||||
|
|
||||||
|
image = await generate_logo(prompt=prompt)
|
||||||
|
|
||||||
|
# 上传到 minio(使用线程池避免阻塞事件循环)
|
||||||
|
file_name = f"{uuid7()}.png"
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
image_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "logo", file_name)
|
||||||
|
return image_url
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
result = asyncio.run(generate_logo_tool.ainvoke({"prompt": "golden retriever"}))
|
||||||
|
print(f"Logo saved to: {result}")
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import asyncio
|
||||||
|
import concurrent.futures
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from PIL import Image
|
||||||
|
from uuid_utils import uuid7
|
||||||
|
from app.service.generate_image.utils.upload_sd_image import upload_SDXL_image
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
PEXELS_API_KEY = os.environ.get("PEXELS_API_KEY", "")
|
||||||
|
PEXELS_BASE_URL = os.environ.get("PEXELS_BASE_URL", "")
|
||||||
|
|
||||||
|
# 线程池用于执行同步上传
|
||||||
|
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
|
||||||
|
|
||||||
|
|
||||||
|
async def search_photos(query: str, per_page: int = 4, user_id: str = "agent") -> list[dict]:
|
||||||
|
"""从 Pexels 搜索图片并上传到 minio
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: 搜索关键词
|
||||||
|
per_page: 返回图片数量 (1-80)
|
||||||
|
user_id: 用户 ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
图片信息列表,每项包含 image_url 和 minio_path
|
||||||
|
"""
|
||||||
|
# 搜索图片
|
||||||
|
async with httpx.AsyncClient(timeout=30) as client:
|
||||||
|
response = await client.get(
|
||||||
|
f"{PEXELS_BASE_URL}/search",
|
||||||
|
headers={"Authorization": PEXELS_API_KEY},
|
||||||
|
params={
|
||||||
|
"query": query,
|
||||||
|
"per_page": per_page,
|
||||||
|
"orientation": "square",
|
||||||
|
"size": "medium",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(f"Pexels API error: {response.status_code} - {response.text}")
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
photos = data.get("photos", [])
|
||||||
|
|
||||||
|
# 下载并上传到 minio
|
||||||
|
results = []
|
||||||
|
for photo in photos:
|
||||||
|
try:
|
||||||
|
# 下载图片(使用 large 尺寸)
|
||||||
|
image_url = photo["src"]["original"]
|
||||||
|
async with httpx.AsyncClient(timeout=60) as dl_client:
|
||||||
|
dl_response = await dl_client.get(image_url)
|
||||||
|
image = Image.open(io.BytesIO(dl_response.content))
|
||||||
|
|
||||||
|
# 上传到 minio(使用线程池避免阻塞事件循环)
|
||||||
|
file_name = f"{uuid7()}.jpg"
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
minio_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "explorer", file_name)
|
||||||
|
results.append({"image_url": image_url, "minio_path": minio_url})
|
||||||
|
logger.info(f"[Explorer] 上传成功: {minio_url}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[Explorer] 上传失败: {e}")
|
||||||
|
|
||||||
|
return results
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import asyncio
|
||||||
|
import concurrent.futures
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from uuid_utils import uuid7
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from app.service.generate_image.utils.upload_sd_image import upload_SDXL_image
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Unsplash API 配置
|
||||||
|
UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY", "")
|
||||||
|
UNSPLASH_BASE_URL = os.environ.get("UNSPLASH_BASE_URL", "")
|
||||||
|
logger = logging.getLogger()
|
||||||
|
# 线程池用于执行同步上传
|
||||||
|
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_random_photos(query: str, count: int = 4, user_id: str = "agent") -> list[dict]:
|
||||||
|
"""从 Unsplash 获取随机图片并上传到 minio
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: 搜索关键词
|
||||||
|
count: 返回图片数量 (1-30)
|
||||||
|
user_id: 用户 ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
图片信息列表,每项包含 image_url 和 minio_path
|
||||||
|
"""
|
||||||
|
# 获取随机图片
|
||||||
|
async with httpx.AsyncClient(timeout=30) as client:
|
||||||
|
response = await client.get(
|
||||||
|
f"{UNSPLASH_BASE_URL}/search/photos",
|
||||||
|
headers={"Authorization": f"Client-ID {UNSPLASH_ACCESS_KEY}"},
|
||||||
|
params={
|
||||||
|
"query": query,
|
||||||
|
"per_page": count,
|
||||||
|
"page": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(f"Unsplash API error: {response.status_code} - {response.text}")
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
# /search/photos 返回 {"results": [...], "total": ...}
|
||||||
|
photos = data.get("results", [])
|
||||||
|
|
||||||
|
# 下载并上传到 minio
|
||||||
|
results = []
|
||||||
|
for photo in photos:
|
||||||
|
try:
|
||||||
|
# 下载图片
|
||||||
|
image_url = photo["urls"]["raw"]
|
||||||
|
async with httpx.AsyncClient(timeout=60) as dl_client:
|
||||||
|
dl_response = await dl_client.get(image_url)
|
||||||
|
image = Image.open(io.BytesIO(dl_response.content))
|
||||||
|
|
||||||
|
# 上传到 minio(使用线程池避免阻塞事件循环)
|
||||||
|
file_name = f"{uuid7()}.jpg"
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
minio_url = await loop.run_in_executor(executor, upload_SDXL_image, image, user_id, "explorer", file_name)
|
||||||
|
results.append({"image_url": image_url, "minio_path": minio_url})
|
||||||
|
logger.info(f"[Explorer] 上传成功: {minio_url}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[Explorer] 上传失败: {e}")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
"""测试 Unsplash 搜索"""
|
||||||
|
query = "summer dress fresh natural style"
|
||||||
|
print(f"搜索关键词: {query}")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
results = await get_random_photos(query, count=4, user_id="test")
|
||||||
|
print(f"\n找到 {len(results)} 张图片:")
|
||||||
|
for i, item in enumerate(results, 1):
|
||||||
|
print(f" {i}. 原图: {item.get('image_url', '')}")
|
||||||
|
print(f" Minio: {item.get('minio_path', '')}")
|
||||||
|
|
||||||
|
asyncio.run(test())
|
||||||
158
app/service/fashion_agent/graph_node/print_graph/graph.py
Normal file
158
app/service/fashion_agent/graph_node/print_graph/graph.py
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
|
||||||
|
from langchain_qwq import ChatQwen
|
||||||
|
from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from app.service.fashion_agent.init_llm import qwen_plus_llm
|
||||||
|
from app.service.fashion_agent.graph_node.print_graph.tools import generate_print_tool, test
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
|
class PrintState(TypedDict):
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
input_text: str
|
||||||
|
role: str = ""
|
||||||
|
gender: str = ""
|
||||||
|
style: str = ""
|
||||||
|
print_need_prompt_generation: bool = False # 是否需要使用 prompt 生成节点
|
||||||
|
|
||||||
|
print_num: int = 1
|
||||||
|
|
||||||
|
print_prompts: list[str] = []
|
||||||
|
print_img_urls: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
"""生成印花图案的提示词节点"""
|
||||||
|
|
||||||
|
|
||||||
|
# 定义输出结构
|
||||||
|
class PrintPrompt(BaseModel):
|
||||||
|
"""生成的印花图像提示词"""
|
||||||
|
|
||||||
|
prompts: list[str] = Field(description="用于生成印花图案的详细提示词")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_input_node(state: PrintState) -> dict:
|
||||||
|
"""从 messages 中提取用户输入"""
|
||||||
|
input_text = state["messages"][0].content if state.get("messages") else ""
|
||||||
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_print_prompt_node(state: PrintState) -> dict:
|
||||||
|
"""根据用户输入生成印花图案的图像生成提示词"""
|
||||||
|
structured_llm = qwen_plus_llm.with_structured_output(PrintPrompt)
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
SystemMessage(content=f"""你是一个专业的印花图案设计师。
|
||||||
|
请根据用户输入,生成用于AI图像生成的印花图案提示词。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 提示词应该详细描述印花图案的样式、元素、颜色、布局
|
||||||
|
2. 提示词应该适合用于 Stable Diffusion 图像生成模型
|
||||||
|
3. 提示词应该使用英文,因为图像生成模型对英文理解更好
|
||||||
|
4. 提示词数量为 {state.get("print_num", 1)}
|
||||||
|
"""),
|
||||||
|
HumanMessage(content=state["input_text"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
result = structured_llm.invoke(messages)
|
||||||
|
prompts = result.prompts
|
||||||
|
logger.info(f"[Print Graph] Generated print prompts: {prompts}")
|
||||||
|
return {
|
||||||
|
"print_prompts": prompts,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"""生成印花图案节点"""
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_print_img_node(state: PrintState) -> dict:
|
||||||
|
"""根据生成的提示词,生成印花图案"""
|
||||||
|
# 如果 print_prompts 为空,使用 input_text 作为 prompt
|
||||||
|
if state.get("print_need_prompt_generation", False):
|
||||||
|
prompts = state["print_prompts"] if state["print_prompts"] else [state["input_text"]]
|
||||||
|
else:
|
||||||
|
input_text = state.get("input_text", "")
|
||||||
|
prompts = [input_text]
|
||||||
|
|
||||||
|
print_img_urls = []
|
||||||
|
for prompt in prompts:
|
||||||
|
image_url = await generate_print_tool.ainvoke({"prompt": prompt})
|
||||||
|
print_img_urls.append(image_url)
|
||||||
|
logger.info(f"[Print Graph] Generated print image URL: {image_url}")
|
||||||
|
|
||||||
|
return {"print_img_urls": print_img_urls}
|
||||||
|
|
||||||
|
|
||||||
|
"""条件分支 判断是否需要生成 prompt"""
|
||||||
|
|
||||||
|
|
||||||
|
def should_generate_prompt(state: PrintState) -> str:
|
||||||
|
"""条件分支:判断是否需要生成 prompt"""
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"[Print Graph] should_generate_prompt: print_need_prompt_generation={state.get('print_need_prompt_generation')}, print_prompts={state.get('print_prompts')}"
|
||||||
|
)
|
||||||
|
if state.get("print_need_prompt_generation", True):
|
||||||
|
return "gen_prompt"
|
||||||
|
else:
|
||||||
|
return "gen_print"
|
||||||
|
|
||||||
|
|
||||||
|
def build_print_graph():
|
||||||
|
|
||||||
|
workflow = StateGraph(PrintState)
|
||||||
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
|
workflow.add_node("gen_prompt", generate_print_prompt_node)
|
||||||
|
workflow.add_node("gen_print", generate_print_img_node)
|
||||||
|
|
||||||
|
# 添加边
|
||||||
|
workflow.add_edge(START, "extract_input")
|
||||||
|
workflow.add_conditional_edges(
|
||||||
|
"extract_input",
|
||||||
|
should_generate_prompt,
|
||||||
|
{
|
||||||
|
"gen_prompt": "gen_prompt",
|
||||||
|
"gen_print": "gen_print",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
workflow.add_edge("gen_prompt", "gen_print")
|
||||||
|
workflow.add_edge("gen_print", END)
|
||||||
|
|
||||||
|
graph = workflow.compile()
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
async def main(test_input, print_need_prompt_generation=True):
|
||||||
|
graph = build_print_graph()
|
||||||
|
result = await graph.ainvoke(
|
||||||
|
{
|
||||||
|
"input_text": test_input,
|
||||||
|
"print_prompts": [] if print_need_prompt_generation else [test_input],
|
||||||
|
"print_need_prompt_generation": print_need_prompt_generation,
|
||||||
|
"role": "",
|
||||||
|
"gender": "",
|
||||||
|
"style": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 测试示例 1: 需要 prompt 生成(默认)
|
||||||
|
test_input = "我想要一个优雅的花卉印花,适合用于连衣裙,颜色以粉色和白色为主"
|
||||||
|
result = asyncio.run(main(test_input, print_need_prompt_generation=True))
|
||||||
|
print("=== 需要 prompt 生成 ===")
|
||||||
|
print(f"Result: {result}")
|
||||||
|
|
||||||
|
# 测试示例 2: 直接使用用户提供的 prompt
|
||||||
|
user_prompt = "Elegant floral print pattern, pink and white colors, suitable for dress fabric, seamless tileable design"
|
||||||
|
result = asyncio.run(main(user_prompt, print_need_prompt_generation=False))
|
||||||
|
print("\n=== 直接使用 prompt ===")
|
||||||
|
print(f"Result: {result}")
|
||||||
39
app/service/fashion_agent/graph_node/print_graph/tools.py
Normal file
39
app/service/fashion_agent/graph_node/print_graph/tools.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
from langchain.tools import tool
|
||||||
|
from langsmith import uuid7
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from app.service.fashion_agent.graph_node.node_tools.generate_image import generate_image
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateImageToolInput(BaseModel):
|
||||||
|
"""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.'")
|
||||||
|
|
||||||
|
|
||||||
|
@tool(args_schema=GenerateImageToolInput)
|
||||||
|
async def generate_print_tool(prompt: str) -> str:
|
||||||
|
"""Generate an image based on the provided prompt."""
|
||||||
|
|
||||||
|
bucket_name = "aida-users"
|
||||||
|
object_name = f"agent_generate_print/{uuid7()}.png"
|
||||||
|
image_url = await generate_image(prompt=prompt, bucket_name=bucket_name, object_name=object_name)
|
||||||
|
return image_url
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
async def test(text: str):
|
||||||
|
"""测试工具函数,返回固定字符串"""
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
async def run_test():
|
||||||
|
result = await generate_print_tool.ainvoke({"prompt": "A cozy living room with warm lighting and natural textures."})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
result = asyncio.run(run_test())
|
||||||
|
print(result)
|
||||||
178
app/service/fashion_agent/graph_node/sketch_graph/graph.py
Normal file
178
app/service/fashion_agent/graph_node/sketch_graph/graph.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
from langchain_qwq import ChatQwen
|
||||||
|
from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage, AIMessage
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from app.service.fashion_agent.init_llm import qwen_plus_llm
|
||||||
|
|
||||||
|
from app.service.fashion_agent.graph_node.sketch_graph.tools import generate_sketch_tool
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
|
class SketchState(TypedDict):
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
input_text: str
|
||||||
|
role: str = ""
|
||||||
|
gender: str = ""
|
||||||
|
style: str = ""
|
||||||
|
sketch_need_prompt_generation: bool = False # 是否需要使用 prompt 生成节点
|
||||||
|
|
||||||
|
sketch_num: int = 1
|
||||||
|
|
||||||
|
sketch_prompts: list[str] = []
|
||||||
|
sketch_img_urls: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
"""生成服装草图的提示词节点"""
|
||||||
|
|
||||||
|
|
||||||
|
# 定义输出结构
|
||||||
|
class SketchPrompt(BaseModel):
|
||||||
|
"""生成的印花图像提示词"""
|
||||||
|
|
||||||
|
prompts: list[str] = Field(description="用于生成服装草图的详细提示词")
|
||||||
|
|
||||||
|
|
||||||
|
def extract_input_node(state: SketchState) -> dict:
|
||||||
|
"""从 messages 中提取用户输入"""
|
||||||
|
input_text = state["messages"][0].content if state.get("messages") else ""
|
||||||
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_sketch_prompt_node(state: SketchState) -> dict:
|
||||||
|
"""根据用户输入生成服装草图的图像生成提示词"""
|
||||||
|
structured_llm = qwen_plus_llm.with_structured_output(SketchPrompt)
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
SystemMessage(content=f"""你是一个专业的服装设计师。
|
||||||
|
请根据用户输入,生成用于AI图像生成的服装草图提示词。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 提示词必须包含:clean black and white line drawing only, pure white background, centered composition
|
||||||
|
2. 提示词应该详细描述服装的廓形、结构、细节
|
||||||
|
3. 提示词应该适合用于 Stable Diffusion 图像生成模型
|
||||||
|
4. 提示词应该使用英文,因为图像生成模型对英文理解更好
|
||||||
|
5. 草图风格必须是黑白线稿,不要添加颜色
|
||||||
|
6. 提示词数量为 {state.get("sketch_num", 1)}
|
||||||
|
"""),
|
||||||
|
HumanMessage(content=state["input_text"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
result = structured_llm.invoke(messages)
|
||||||
|
prompts = result.prompts
|
||||||
|
|
||||||
|
return {
|
||||||
|
"sketch_prompts": prompts,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"""生成服装草图节点"""
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_sketch_img_node(state: SketchState) -> dict:
|
||||||
|
"""根据生成的提示词,生成服装草图"""
|
||||||
|
# 如果 sketch_need_prompt_generation=False 且 sketch_prompts 为空,使用模板生成 prompt
|
||||||
|
# if not state.get("sketch_need_prompt_generation", False) and not state.get("sketch_prompts"):
|
||||||
|
|
||||||
|
# input_text = state.get("input_text", "")
|
||||||
|
# prompts = [build_sketch_template_prompt(input_text)]
|
||||||
|
# else:
|
||||||
|
# prompts = state["sketch_prompts"] if state["sketch_prompts"] else [state["input_text"]]
|
||||||
|
|
||||||
|
# sketch_img_urls = []
|
||||||
|
# for prompt in prompts:
|
||||||
|
# image_url = await generate_sketch_tool.ainvoke({"prompt": prompt})
|
||||||
|
# sketch_img_urls.append(image_url)
|
||||||
|
|
||||||
|
# result_text = f"服装草图生成完成,共生成 {len(sketch_img_urls)} 张图片:\n" + "\n".join(sketch_img_urls)
|
||||||
|
# return {"sketch_img_urls": sketch_img_urls, "messages": [AIMessage(content=result_text)]}
|
||||||
|
return {"messages": [AIMessage(content="hello")]}
|
||||||
|
|
||||||
|
|
||||||
|
"""条件分支 判断是否需要生成 prompt"""
|
||||||
|
|
||||||
|
|
||||||
|
def should_generate_prompt(state: SketchState) -> str:
|
||||||
|
"""条件分支:判断是否需要生成 prompt"""
|
||||||
|
if state.get("sketch_need_prompt_generation", False):
|
||||||
|
return "gen_prompt"
|
||||||
|
else:
|
||||||
|
return "gen_sketch"
|
||||||
|
|
||||||
|
|
||||||
|
def build_sketch_graph():
|
||||||
|
workflow = StateGraph(SketchState)
|
||||||
|
workflow.add_node("gen_sketch", generate_sketch_img_node)
|
||||||
|
workflow.add_edge(START, "gen_sketch")
|
||||||
|
workflow.add_edge("gen_sketch", END)
|
||||||
|
graph = workflow.compile()
|
||||||
|
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:
|
||||||
|
"""构建 sketch prompt 模板"""
|
||||||
|
return f"{input_text}, clean black and white line drawing only, pure white background, centered composition, fashion sketch style"
|
||||||
|
|
||||||
|
|
||||||
|
async def main(test_input, sketch_need_prompt_generation=False):
|
||||||
|
graph = build_sketch_graph()
|
||||||
|
|
||||||
|
# 如果不需要 LLM 生成 prompt,使用模板
|
||||||
|
if not sketch_need_prompt_generation:
|
||||||
|
sketch_prompts = [build_sketch_template_prompt(test_input)]
|
||||||
|
else:
|
||||||
|
sketch_prompts = []
|
||||||
|
|
||||||
|
result = await graph.ainvoke(
|
||||||
|
{
|
||||||
|
"input_text": test_input,
|
||||||
|
"sketch_prompts": sketch_prompts,
|
||||||
|
"sketch_need_prompt_generation": sketch_need_prompt_generation,
|
||||||
|
"role": "",
|
||||||
|
"gender": "",
|
||||||
|
"style": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 测试示例 1: 直接使用模板 prompt(默认)
|
||||||
|
test_input = "dress"
|
||||||
|
result = asyncio.run(main(test_input, sketch_need_prompt_generation=False))
|
||||||
|
print("=== 使用模板 prompt ===")
|
||||||
|
print(f"Result: {result}")
|
||||||
|
|
||||||
|
# # 测试示例 2: 使用 LLM 生成 prompt
|
||||||
|
# test_input = "设计一条优雅的A字廓形连衣裙,V领设计,收腰,裙摆到膝盖,适合日常穿着"
|
||||||
|
# result = asyncio.run(main(test_input, sketch_need_prompt_generation=True))
|
||||||
|
# print("\n=== 使用 LLM 生成 prompt ===")
|
||||||
|
# print(f"Result: {result}")
|
||||||
33
app/service/fashion_agent/graph_node/sketch_graph/tools.py
Normal file
33
app/service/fashion_agent/graph_node/sketch_graph/tools.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
from langchain.tools import tool
|
||||||
|
from langsmith import uuid7
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from app.service.fashion_agent.graph_node.node_tools.generate_image import generate_image
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateImageToolInput(BaseModel):
|
||||||
|
"""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.'")
|
||||||
|
|
||||||
|
|
||||||
|
@tool(args_schema=GenerateImageToolInput)
|
||||||
|
async def generate_sketch_tool(prompt: str) -> str:
|
||||||
|
"""Generate an image based on the provided prompt."""
|
||||||
|
|
||||||
|
bucket_name = "fida-public-bucket"
|
||||||
|
object_name = f"test/{uuid7()}.png"
|
||||||
|
image_url = await generate_image(prompt=prompt, bucket_name=bucket_name, object_name=object_name)
|
||||||
|
return image_url
|
||||||
|
|
||||||
|
|
||||||
|
async def run_test():
|
||||||
|
result = await generate_sketch_tool.ainvoke({"prompt": "A cozy living room with warm lighting and natural textures."})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
result = asyncio.run(run_test())
|
||||||
|
print(result)
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import asyncio
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
|
||||||
|
from langchain_core.messages import AIMessage, AnyMessage, HumanMessage
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
|
||||||
|
"""定义状态"""
|
||||||
|
|
||||||
|
|
||||||
|
class TrendingState(TypedDict):
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
input_text: str
|
||||||
|
|
||||||
|
|
||||||
|
"""节点"""
|
||||||
|
|
||||||
|
|
||||||
|
def extract_input_node(state: TrendingState) -> dict:
|
||||||
|
"""从 messages 中提取用户输入"""
|
||||||
|
input_text = state["messages"][0].content if state.get("messages") else ""
|
||||||
|
return {"input_text": input_text}
|
||||||
|
|
||||||
|
|
||||||
|
async def trending_node(state: TrendingState) -> dict:
|
||||||
|
"""趋势分析节点(占位)"""
|
||||||
|
input_text = state.get("input_text", "")
|
||||||
|
|
||||||
|
# TODO: 接入真实的趋势分析逻辑
|
||||||
|
result_text = (
|
||||||
|
f"【趋势分析】\n基于您的输入「{input_text}」,以下是当前服装设计趋势:\n\n"
|
||||||
|
"1. 极简主义持续流行,黑白灰为主色调\n"
|
||||||
|
"2. 可持续时尚成为主流,环保面料受青睐\n"
|
||||||
|
"3. 复古风格回潮,90年代元素重新流行\n"
|
||||||
|
"4. 功能性与美学结合,运动休闲风持续升温"
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"messages": [AIMessage(content=result_text)]}
|
||||||
|
|
||||||
|
|
||||||
|
"""构建图"""
|
||||||
|
|
||||||
|
|
||||||
|
def build_trending_graph():
|
||||||
|
"""构建趋势分析图"""
|
||||||
|
workflow = StateGraph(TrendingState)
|
||||||
|
|
||||||
|
workflow.add_node("extract_input", extract_input_node)
|
||||||
|
workflow.add_node("trending", trending_node)
|
||||||
|
|
||||||
|
workflow.add_edge(START, "extract_input")
|
||||||
|
workflow.add_edge("extract_input", "trending")
|
||||||
|
workflow.add_edge("trending", END)
|
||||||
|
|
||||||
|
return workflow.compile()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
graph = build_trending_graph()
|
||||||
|
result = await graph.ainvoke(
|
||||||
|
{
|
||||||
|
"messages": [HumanMessage(content="女装连衣裙")],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(result["messages"][-1].content)
|
||||||
|
|
||||||
|
asyncio.run(test())
|
||||||
31
app/service/fashion_agent/init_llm.py
Normal file
31
app/service/fashion_agent/init_llm.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from langchain_qwq import ChatQwen
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
QWEN_API_KEY_INTL = os.environ.get("QWEN_API_KEY_INTL", "")
|
||||||
|
|
||||||
|
|
||||||
|
def build_llm(enable_thinking: bool = False):
|
||||||
|
llm = ChatQwen(
|
||||||
|
model="qwen3.6-plus",
|
||||||
|
timeout=None,
|
||||||
|
max_retries=2,
|
||||||
|
enable_thinking=enable_thinking,
|
||||||
|
streaming=True,
|
||||||
|
api_key=QWEN_API_KEY_INTL,
|
||||||
|
)
|
||||||
|
return llm
|
||||||
|
|
||||||
|
|
||||||
|
qwen_plus_llm = ChatQwen(
|
||||||
|
model="qwen-plus",
|
||||||
|
timeout=None,
|
||||||
|
max_retries=2,
|
||||||
|
streaming=False,
|
||||||
|
temperature=0.25,
|
||||||
|
top_p=0.8,
|
||||||
|
api_key=QWEN_API_KEY_INTL,
|
||||||
|
)
|
||||||
144
app/service/fashion_agent/main_agent.py
Normal file
144
app/service/fashion_agent/main_agent.py
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Annotated, Required, TypedDict
|
||||||
|
from deepagents import CompiledSubAgent, create_deep_agent
|
||||||
|
from langchain.agents import create_agent
|
||||||
|
from langchain.tools import tool
|
||||||
|
from langchain_core.messages import AnyMessage, HumanMessage
|
||||||
|
from langchain_qwq import ChatQwen
|
||||||
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
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.tools import design_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.node_tools.generate_logo 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.tools import generate_print_tool
|
||||||
|
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.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.init_llm import build_llm
|
||||||
|
|
||||||
|
print_graph = build_print_graph()
|
||||||
|
logo_graph = build_logo_graph()
|
||||||
|
sketch_graph = build_sketch_graph()
|
||||||
|
design_graph = build_design_graph()
|
||||||
|
trending_graph = build_trending_graph()
|
||||||
|
explorer_graph = build_explorer_graph()
|
||||||
|
|
||||||
|
|
||||||
|
class MainState(TypedDict):
|
||||||
|
# 消息
|
||||||
|
messages: Required[Annotated[list[AnyMessage], add_messages]]
|
||||||
|
|
||||||
|
# 模块控制
|
||||||
|
call_design: bool = False
|
||||||
|
call_print: bool = False
|
||||||
|
call_logo: bool = False
|
||||||
|
call_sketch: bool = False
|
||||||
|
call_design: bool = False
|
||||||
|
call_trending: bool = False
|
||||||
|
call_explor: bool = False
|
||||||
|
|
||||||
|
# design参数
|
||||||
|
design_request_data: dict = {}
|
||||||
|
|
||||||
|
# 模块需求标志
|
||||||
|
print_need_prompt_generation: bool = False
|
||||||
|
sketch_need_prompt_generation: bool = False
|
||||||
|
|
||||||
|
# 公共参数
|
||||||
|
role: str = ""
|
||||||
|
gender: str = ""
|
||||||
|
style: str = ""
|
||||||
|
|
||||||
|
# print模块结果
|
||||||
|
print_img_urls: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
tools = [explor_tool, generate_logo_tool, generate_print_tool, generate_sketch_tool]
|
||||||
|
|
||||||
|
|
||||||
|
def route_node(state: MainState) -> str:
|
||||||
|
"""根据标志决定走哪条路径"""
|
||||||
|
if state.get("call_print"):
|
||||||
|
return "direct_print"
|
||||||
|
if state.get("call_logo"):
|
||||||
|
return "direct_logo"
|
||||||
|
if state.get("call_sketch"):
|
||||||
|
return "direct_sketch"
|
||||||
|
if state.get("call_design"):
|
||||||
|
return "direct_design"
|
||||||
|
if state.get("call_trending"):
|
||||||
|
return "direct_trending"
|
||||||
|
if state.get("call_explor"):
|
||||||
|
return "direct_explor"
|
||||||
|
return "llm_agent"
|
||||||
|
|
||||||
|
|
||||||
|
def build_main_graph(enable_thinking: bool = False):
|
||||||
|
llm = build_llm(enable_thinking=enable_thinking)
|
||||||
|
chat_agent = create_agent(
|
||||||
|
model=llm, tools=tools, state_schema=MainState, system_prompt="你是一个专业的服装设计助手。根据用户需求,调用合适的工具完成任务."
|
||||||
|
)
|
||||||
|
|
||||||
|
"""构建主图"""
|
||||||
|
workflow = StateGraph(MainState)
|
||||||
|
|
||||||
|
# 添加节点
|
||||||
|
workflow.add_node("llm_agent", chat_agent)
|
||||||
|
workflow.add_node("direct_print", print_graph)
|
||||||
|
workflow.add_node("direct_logo", logo_graph)
|
||||||
|
workflow.add_node("direct_sketch", sketch_graph)
|
||||||
|
workflow.add_node("direct_design", design_graph)
|
||||||
|
workflow.add_node("direct_trending", trending_graph)
|
||||||
|
workflow.add_node("direct_explor", explorer_graph)
|
||||||
|
|
||||||
|
# 条件分支
|
||||||
|
workflow.add_conditional_edges(
|
||||||
|
START,
|
||||||
|
route_node,
|
||||||
|
{
|
||||||
|
"llm_agent": "llm_agent",
|
||||||
|
"direct_print": "direct_print",
|
||||||
|
"direct_logo": "direct_logo",
|
||||||
|
"direct_sketch": "direct_sketch",
|
||||||
|
"direct_design": "direct_design",
|
||||||
|
"direct_trending": "direct_trending",
|
||||||
|
"direct_explor": "direct_explor",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# 所有路径都到 END
|
||||||
|
workflow.add_edge("llm_agent", END)
|
||||||
|
workflow.add_edge("direct_print", END)
|
||||||
|
workflow.add_edge("direct_logo", END)
|
||||||
|
workflow.add_edge("direct_sketch", END)
|
||||||
|
workflow.add_edge("direct_design", END)
|
||||||
|
workflow.add_edge("direct_trending", END)
|
||||||
|
workflow.add_edge("direct_explor", END)
|
||||||
|
|
||||||
|
return workflow.compile()
|
||||||
|
|
||||||
|
|
||||||
|
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())
|
||||||
132
app/service/fashion_agent/service.py
Normal file
132
app/service/fashion_agent/service.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from langgraph.stream import ProtocolEvent, StreamChannel, StreamTransformer
|
||||||
|
from app.service.fashion_agent.main_agent import build_main_graph
|
||||||
|
from langgraph.prebuilt import ToolCallTransformer
|
||||||
|
from typing import AsyncGenerator, TypedDict
|
||||||
|
from langchain_core.messages import HumanMessage
|
||||||
|
from app.schemas.fashion_agent import FashionAgentRequest
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class CustomTransformer(StreamTransformer):
|
||||||
|
required_stream_modes = ("custom",)
|
||||||
|
|
||||||
|
def __init__(self, scope: tuple[str, ...] = ()) -> None:
|
||||||
|
super().__init__(scope)
|
||||||
|
self.log = StreamChannel()
|
||||||
|
|
||||||
|
def init(self) -> dict:
|
||||||
|
return {"custom": self.log}
|
||||||
|
|
||||||
|
def process(self, event: ProtocolEvent) -> bool:
|
||||||
|
if event["method"] == "custom":
|
||||||
|
self.log.push(event["params"]["data"])
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class FashionAgentService:
|
||||||
|
|
||||||
|
async def run_stream(self, request: FashionAgentRequest) -> AsyncGenerator[str, None]:
|
||||||
|
"""流式运行 agent - 使用 v3 projections"""
|
||||||
|
|
||||||
|
config = {"configurable": {"user_id": request.user_id}}
|
||||||
|
|
||||||
|
agent = build_main_graph(enable_thinking=request.enable_thinking)
|
||||||
|
state = {
|
||||||
|
"messages": [HumanMessage(content=request.message)],
|
||||||
|
"call_print": request.call_print,
|
||||||
|
"call_logo": request.call_logo,
|
||||||
|
"call_sketch": request.call_sketch,
|
||||||
|
"call_design": request.call_design,
|
||||||
|
"call_trending": request.call_trending,
|
||||||
|
"call_explor": request.call_explor,
|
||||||
|
"print_need_prompt_generation": request.print_need_prompt_generation,
|
||||||
|
"sketch_need_prompt_generation": request.sketch_need_prompt_generation,
|
||||||
|
"design_request_data": request.design_request_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = await agent.astream_events(state, config=config, version="v3", transformers=[ToolCallTransformer, CustomTransformer])
|
||||||
|
|
||||||
|
tool_names = {}
|
||||||
|
filter_tool_name = ["design_tool"]
|
||||||
|
async for event in stream:
|
||||||
|
if event["method"] == "tools":
|
||||||
|
data = event["params"]["data"]
|
||||||
|
tool_call_id = data.get("tool_call_id")
|
||||||
|
|
||||||
|
# 记录 tool_name
|
||||||
|
if data.get("event") == "tool-started":
|
||||||
|
tool_names[tool_call_id] = data.get("tool_name")
|
||||||
|
|
||||||
|
# 通过 ID 查找 tool_name
|
||||||
|
elif data.get("event") == "tool-finished":
|
||||||
|
tool_name = tool_names.get(tool_call_id, "unknown")
|
||||||
|
|
||||||
|
if tool_name in filter_tool_name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data["tool_name"] = tool_name
|
||||||
|
|
||||||
|
response_event = {"event_type": "tool", "data": data}
|
||||||
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
|
elif event["method"] == "custom":
|
||||||
|
data = event["params"]["data"]
|
||||||
|
response_event = {"event_type": "tool", "data": data}
|
||||||
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
|
elif event["method"] == "messages":
|
||||||
|
data = event["params"]["data"][0]
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
continue
|
||||||
|
if data.get("event") != "content-block-delta":
|
||||||
|
continue
|
||||||
|
block = data.get("delta") or {}
|
||||||
|
|
||||||
|
if block.get("type") == "text-delta":
|
||||||
|
response_event = {"event_type": "messages", "data": {"event": data["event"]} | block}
|
||||||
|
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}
|
||||||
|
yield f"data: {json.dumps(response_event, ensure_ascii=False)}\n\n"
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# print(f"----------------{event}")
|
||||||
|
response_event = {"event_type": "done"}
|
||||||
|
yield f"data: {response_event}"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def test_stream():
|
||||||
|
"""测试流式调用"""
|
||||||
|
|
||||||
|
with open("app/service/fashion_agent/graph_node/design_graph/design_request_data.json", encoding="utf-8") as f:
|
||||||
|
request_data = json.load(f)
|
||||||
|
|
||||||
|
service = FashionAgentService()
|
||||||
|
|
||||||
|
print("=" * 50)
|
||||||
|
print("测试流式输出")
|
||||||
|
print("=" * 50)
|
||||||
|
request = FashionAgentRequest(
|
||||||
|
message="生成一张草莓图案",
|
||||||
|
call_print=True,
|
||||||
|
# print_need_prompt_generation=False,
|
||||||
|
# call_sketch=True,
|
||||||
|
# sketch_need_prompt_generation=False,
|
||||||
|
# call_logo=True,
|
||||||
|
# call_explor=True,
|
||||||
|
# call_design=True,
|
||||||
|
# design_request_data=request_data,
|
||||||
|
)
|
||||||
|
async for chunk in service.run_stream(request):
|
||||||
|
print(chunk, end="")
|
||||||
|
|
||||||
|
# 运行测试
|
||||||
|
asyncio.run(test_stream())
|
||||||
@@ -92,7 +92,7 @@ if __name__ == "__main__":
|
|||||||
# url = "aida-users/89/sketchboard/female/Dress/e6724ab7-8d3f-4677-abe0-c3e42ab7af85.jpeg"
|
# url = "aida-users/89/sketchboard/female/Dress/e6724ab7-8d3f-4677-abe0-c3e42ab7af85.jpeg"
|
||||||
# url = "aida-users/87/print/956614a2-7e75-4fbe-9ed0-c1831e37a2c9-4-87.png"
|
# url = "aida-users/87/print/956614a2-7e75-4fbe-9ed0-c1831e37a2c9-4-87.png"
|
||||||
# url = "aida-users/89/single_logo/123-89.png"
|
# url = "aida-users/89/single_logo/123-89.png"
|
||||||
url = "aida-collection-element/26293/Sketchboard/b503d482-3334-46e7-9dee-44e380fb4294.png"
|
url = "aida-users/agent/logo/019e91c4-7b1f-74e2-b716-d652713101cd.png"
|
||||||
|
|
||||||
# url = "aida-collection-element/12148/Sketchboard/95ea577b-305b-4a62-b30a-39c0dd3ddb3f.png"
|
# url = "aida-collection-element/12148/Sketchboard/95ea577b-305b-4a62-b30a-39c0dd3ddb3f.png"
|
||||||
read_type = "2"
|
read_type = "2"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ dependencies = [
|
|||||||
"langchain-community>=0.4.1",
|
"langchain-community>=0.4.1",
|
||||||
"langchain-qwq>=0.3.5",
|
"langchain-qwq>=0.3.5",
|
||||||
"langgraph>=1.0.5",
|
"langgraph>=1.0.5",
|
||||||
|
"langgraph-api>=0.4.28",
|
||||||
"langgraph-cli[inmem,redis]<=0.4.26",
|
"langgraph-cli[inmem,redis]<=0.4.26",
|
||||||
"load>=1.0.14",
|
"load>=1.0.14",
|
||||||
"load-dotenv>=0.1.0",
|
"load-dotenv>=0.1.0",
|
||||||
|
|||||||
215
uv.lock
generated
215
uv.lock
generated
@@ -83,7 +83,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
version = "3.14.0"
|
version = "3.14.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohappyeyeballs" },
|
{ name = "aiohappyeyeballs" },
|
||||||
@@ -95,26 +95,26 @@ dependencies = [
|
|||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
{ name = "yarl" },
|
{ name = "yarl" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/ab/93ce242f899b68c51b0578c027aafa791ab3614cb9345fa5d37b5f5c8e3e/aiohttp-3.14.0.tar.gz", hash = "sha256:2882de819734c715fd1b9c11c97e09fa020d14438203d1d354d8ed1702791c9b", size = 7940674, upload-time = "2026-06-01T19:41:02.763Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/89/97/2b6889bfb6b6847520d50d95eb8c4307a45e28aaca39faf4a9454b3d1b2f/aiohttp-3.14.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b29518c9c2ec7e373e68259206a137c7f4f5439c58baaec4b5ab3ab799850a4e", size = 750194, upload-time = "2026-06-01T19:37:48.164Z" },
|
{ url = "https://files.pythonhosted.org/packages/1d/21/151624b51cd92553d95424daf4bf19f19ce9be9002d19253e7e7ce67197b/aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480", size = 757402, upload-time = "2026-06-07T21:06:40.311Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/21/e2/62634b7fff918ed98c3c6b2f0e70d520f7f28846cb412d451b04354c6459/aiohttp-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbec68ce61b64cb73cab4d33df9433427b1713c8bcccb181dce695c1b6f8e87c", size = 506966, upload-time = "2026-06-01T19:37:50.014Z" },
|
{ url = "https://files.pythonhosted.org/packages/c2/82/280619e0bd7bf2454987e19282616e84762255dd9c8468f62382e8c191f1/aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d", size = 512310, upload-time = "2026-06-07T21:06:42.207Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/dd/fb/5ce075150828c797a5106f1c2fb26034e709d4289b9d2bf8b07f1e59fac6/aiohttp-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cdf534aa455593e589302990c5097aa5c92c06c4262a20da22934f9186a5fff", size = 507527, upload-time = "2026-06-01T19:37:51.96Z" },
|
{ url = "https://files.pythonhosted.org/packages/55/b2/2aac325583aaa1353045f96dffa586d8a34e8322e14a7ba49cffeb103ab4/aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2", size = 512448, upload-time = "2026-06-07T21:06:43.813Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/01/d5/405a0ae4e6b081754a3609c1c97c63a950e000a2def16046f1e736933a0e/aiohttp-3.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb6c657104393b5fbff01a5f59b2023db74058a8077d94475d6c25d03882a108", size = 1762420, upload-time = "2026-06-01T19:37:53.839Z" },
|
{ url = "https://files.pythonhosted.org/packages/8a/72/a60607cb849faa8af8a356c9329ea2eb6f395d49e82cc82ccba1fd8deb8f/aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2", size = 1766854, upload-time = "2026-06-07T21:06:45.391Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ae/1d/e05a7c896b15a6bc6fb8fc5319eb437861c2c49c34559ef928add6590315/aiohttp-3.14.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:46fbbec4e4fab7428d4396a3823f9320e4560aa3113b89eeebce712c27c9ed5a", size = 1733672, upload-time = "2026-06-01T19:37:55.791Z" },
|
{ url = "https://files.pythonhosted.org/packages/b5/d3/d9fe1c9ec7557ab4d0d82bebaa728c6418f0b93295ec2f4ab015f7710cc7/aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a", size = 1740884, upload-time = "2026-06-07T21:06:47.413Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/cc/22/a72f7c459e195fa41bf4f7abd1f925b91fe91f8097e51c654229ba144a33/aiohttp-3.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2c2c7e05dd5335b298085abf45ddf98673934c3ee1c083d0b9ea13d4186ad500", size = 1805064, upload-time = "2026-06-01T19:37:57.931Z" },
|
{ url = "https://files.pythonhosted.org/packages/c1/dc/f2cecfaf9337ba3e63f181500814ff502aa3d00d9c7ec93a9d23d10a27b2/aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264", size = 1810034, upload-time = "2026-06-07T21:06:50.165Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/80/50/e85bdaba0be59ca4838005ebfef4048fcdd5f35a02b07057a9a123394440/aiohttp-3.14.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c7139100fbaae76515b73051d8f0aa3a3ff02e415eec8a8eee8e2223d9ba955", size = 1902125, upload-time = "2026-06-01T19:38:00.225Z" },
|
{ url = "https://files.pythonhosted.org/packages/66/d7/2ff65c5e65c0d7476daf7e15c032e0805e36811185b9623e3238ad6c763e/aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842", size = 1904054, upload-time = "2026-06-07T21:06:52.035Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/19/d8/51de5c6b971c27bb1ef620293b8d1ca611ec78736b34b3f6ccf68e4c8785/aiohttp-3.14.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78d6f9286a629ce52728430afe18f8ed2b6c39a1fddb3802d7244b9983910ad2", size = 1783112, upload-time = "2026-06-01T19:38:02.641Z" },
|
{ url = "https://files.pythonhosted.org/packages/20/9c/d445818389df371f56d141d881153ba23183c4735a03f7356ffb43f7757d/aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c", size = 1790278, upload-time = "2026-06-07T21:06:54.049Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/ae/b4402bfde77e43dfb1b6ccff83c7b7ab63ed06b50c4754f0c5423fb374fe/aiohttp-3.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3c3e12cdaeb92d7dcf13db00e9f6b1956b910e47256e696df1cfa946d02159", size = 1586356, upload-time = "2026-06-01T19:38:04.637Z" },
|
{ url = "https://files.pythonhosted.org/packages/4d/aa/bf04cb4d865fc6101c2229a294ad744973b72e513fdc5a6b791e6983d72a/aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95", size = 1591795, upload-time = "2026-06-07T21:06:55.911Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bc/05/750a3265ca4dc54a460bd0cb1121a8f2ce9171fce4a135fb47ea7fd594d2/aiohttp-3.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d6a998191f5ebe3b8c28463ff72bc030250008b3193c402464efadd08b5ca02", size = 1723119, upload-time = "2026-06-01T19:38:06.713Z" },
|
{ url = "https://files.pythonhosted.org/packages/dc/b4/4dac0038960427ba832f6609dfb4ea5437d7fd80c72001b9e48f834f428b/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199", size = 1728397, upload-time = "2026-06-07T21:06:57.777Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/37/01/8c0812c50b3b1b1c37b323bf170d6be8847a8f234060485b7d1e71953f60/aiohttp-3.14.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0fc2b75ae8d169d853be2862d960be8550da6c5c65711d5476407eb3fdb006bd", size = 1757216, upload-time = "2026-06-01T19:38:08.736Z" },
|
{ url = "https://files.pythonhosted.org/packages/2b/f9/7cd4e8ad7aa3b75f17d56bb5498dd604a93d4e6eece822ba0568c413fff0/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817", size = 1766504, upload-time = "2026-06-07T21:07:00.009Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/2a/50fb98028a26887cbe48dcc1df92a90825615bc73b5584301304090cded8/aiohttp-3.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:16eee56bcc72d04600bc56c1759982c2385ec0b41d3fd3521f836bf64a0957ef", size = 1770500, upload-time = "2026-06-01T19:38:11.111Z" },
|
{ url = "https://files.pythonhosted.org/packages/f9/df/fc01d9fcad0f73fed3f3d361f1f94f975947b50dff82919f6dc2bf4316cc/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a", size = 1777806, upload-time = "2026-06-07T21:07:02.064Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bd/32/0ffd598a2fa2b9a423daf242e700cfdabda35d6e602394ad9ae58972c1c7/aiohttp-3.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5a2e7ca615c3ddc15b82687e05a624e5f5cba3f1d6c20cb81172d70ea498451e", size = 1576224, upload-time = "2026-06-01T19:38:13.391Z" },
|
{ url = "https://files.pythonhosted.org/packages/41/09/47e2d090bddcc8fb4ccb4c314aadc32d7c5d9bb55f50f6ad1c92fc15d501/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4", size = 1580707, upload-time = "2026-06-07T21:07:03.942Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0b/f9/b9fc381dd9b66afb33f2634c40e229d106467be0afcabe79648631ab6712/aiohttp-3.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0b7b8bbbec3ce9467ee0ebe334622fd90624f593edd3136c567811453fc4fae", size = 1794252, upload-time = "2026-06-01T19:38:15.498Z" },
|
{ url = "https://files.pythonhosted.org/packages/3d/36/f1a4ce904ae0b6930cfe9afc96d0896f7ec1a620c400405d63783bb95a9c/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087", size = 1798121, upload-time = "2026-06-07T21:07:05.987Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a8/fb/05d9214c975f23225a8cd5c439325e338c7c377b315480ef3871db51f54e/aiohttp-3.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ba10966d4f03dd96a14365be4b8e37c327c76f11c3ca867116966cdd9f98066", size = 1760193, upload-time = "2026-06-01T19:38:17.624Z" },
|
{ url = "https://files.pythonhosted.org/packages/70/0a/e0075ce9ca0279ee1d4f0c0b85f54fea02ebc83c3007651a72bece658fec/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3", size = 1767580, upload-time = "2026-06-07T21:07:07.873Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d9/4b/02992fc4fb9e1b6673ee3f888a8e587a6447afda1f6f4aca776c148c2876/aiohttp-3.14.0-cp312-cp312-win32.whl", hash = "sha256:101df7779c80c0636014a6b2c6642acd3efb5b355d48347c9d7dfb720aee9430", size = 448650, upload-time = "2026-06-01T19:38:19.545Z" },
|
{ url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771, upload-time = "2026-06-07T21:07:09.669Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/39/e9/246532214c3abda518477cbaaf16d420295ad8effa5233844cbb38f299ab/aiohttp-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:b0a5747586d4467efd1f932710b269131c9717a872dce082cd92a00c1c13123a", size = 476145, upload-time = "2026-06-01T19:38:21.505Z" },
|
{ url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873, upload-time = "2026-06-07T21:07:11.538Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2b/c3/63f8c20090048915711598b0adf475b149216d736157961de06480a45b15/aiohttp-3.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:5f1c5be60add78fabb4aacd13c5a348ae79d2fcbfc7fa78da8f1eb192273b370", size = 444250, upload-time = "2026-06-01T19:38:24.027Z" },
|
{ url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073, upload-time = "2026-06-07T21:07:13.637Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -323,7 +323,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anthropic"
|
name = "anthropic"
|
||||||
version = "0.105.2"
|
version = "0.107.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
@@ -335,9 +335,9 @@ dependencies = [
|
|||||||
{ name = "sniffio" },
|
{ name = "sniffio" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/46/46/47581b8c689c743ceabf6a0f9ff48472160900ce802d26c0fb50423997b3/anthropic-0.105.2.tar.gz", hash = "sha256:0e26b90841c2dced7cc6e98d21d5517d0be33f1876b8e779f478202e28bcaa07", size = 853789, upload-time = "2026-05-29T00:21:14.104Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b1/f1/c6076a92e0bf6b0dfa126e213b3f9e8a510acd73567953210713aae6c256/anthropic-0.107.1.tar.gz", hash = "sha256:8e7169a6ab57fb806b778d9af018c867bad688144efec8969cdb4c5ccecd6670", size = 856312, upload-time = "2026-06-07T17:18:57.358Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/83/75/be0c357e33a5a56c8f9db5b4212f886138d2bf59c0952d858f6b75d710ef/anthropic-0.105.2-py3-none-any.whl", hash = "sha256:e53ed5f6bf36fb1ecb9b25d8634cfd30e02fab9fb3374a0c2d5c585874757230", size = 837507, upload-time = "2026-05-29T00:21:15.528Z" },
|
{ url = "https://files.pythonhosted.org/packages/86/0e/71432f0777a263701955a23ebcc6650485c2753be9afbce2a6a8d72526e3/anthropic-0.107.1-py3-none-any.whl", hash = "sha256:b74338d08000ba105dfc8adae29af3713ece845a4bffec9986a20697e087c7b3", size = 838729, upload-time = "2026-06-07T17:18:58.729Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -465,15 +465,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beautifulsoup4"
|
name = "beautifulsoup4"
|
||||||
version = "4.14.3"
|
version = "4.15.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "soupsieve" },
|
{ name = "soupsieve" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
|
{ url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -919,7 +919,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashscope"
|
name = "dashscope"
|
||||||
version = "1.25.20"
|
version = "1.25.21"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
@@ -931,7 +931,7 @@ dependencies = [
|
|||||||
{ name = "websocket-client" },
|
{ name = "websocket-client" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/4c/49/194907e6d4a8a159445c2f3234568f5d1816df46c31b65a4d4ecc569a4ef/dashscope-1.25.20-py3-none-any.whl", hash = "sha256:1d1257f205433f85ddc44a3193e6a99d1372071e34a17d284c2c5c421d44a7e3", size = 1477164, upload-time = "2026-06-01T10:23:21.234Z" },
|
{ url = "https://files.pythonhosted.org/packages/0f/22/a174358052f43aa6ad5183eeda44da5cc3ffac472f220dfba4b67f296267/dashscope-1.25.21-py3-none-any.whl", hash = "sha256:a730b3e9e41fb4261ab89192f667964aee2057779d87c26beaa9889aa07afbb9", size = 1479509, upload-time = "2026-06-04T08:13:27.256Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -945,7 +945,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deepagents"
|
name = "deepagents"
|
||||||
version = "0.6.7"
|
version = "0.6.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "langchain" },
|
{ name = "langchain" },
|
||||||
@@ -955,9 +955,9 @@ dependencies = [
|
|||||||
{ name = "langsmith" },
|
{ name = "langsmith" },
|
||||||
{ name = "wcmatch" },
|
{ name = "wcmatch" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/51/bb/bb837a2c51631fe9d7eedf6aca7629ddca6336831801e75efcd2f5fa9c27/deepagents-0.6.7.tar.gz", hash = "sha256:af7b5857b28e29a847a4ced4cc7aaa809d33d42107696b1ca5d978d17e96b831", size = 194236, upload-time = "2026-05-30T04:42:14.591Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/97/77/e3b7efd9bff9cd101c085a5a3bf74180c13ab6c41a96f725cd1cb1bf53e8/deepagents-0.6.8.tar.gz", hash = "sha256:70cdd4da920cc420a8a0f729792ec559688bbbff39f7ab1508110cce9f901c06", size = 196927, upload-time = "2026-06-03T17:08:36.724Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/97/25/3a7f23d04778ccb95542f1b1ed39826290916221cc8203d0fb8b26c3edc1/deepagents-0.6.7-py3-none-any.whl", hash = "sha256:3518c1e5f4b9f6588ba39912b668b42b5c864b99a638e94c166d1c7176c7388e", size = 218740, upload-time = "2026-05-30T04:42:13.225Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/19/1b7b76e958ac7f4e40886edc70f67aff4d7188770ab68105c9c48cbeb769/deepagents-0.6.8-py3-none-any.whl", hash = "sha256:087bdc1458202a3436854cf180f7ec059d07d2114a6c232819e9ad6533a5174a", size = 221469, upload-time = "2026-06-03T17:08:35.133Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -980,16 +980,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django"
|
name = "django"
|
||||||
version = "6.0.5"
|
version = "6.0.6"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "asgiref" },
|
{ name = "asgiref" },
|
||||||
{ name = "sqlparse" },
|
{ name = "sqlparse" },
|
||||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/f1/bf85f0d29ef76abf901f193fe8fef4769d3da7794197832bc30151c071d8/django-6.0.5.tar.gz", hash = "sha256:bc6d6872e98a2864c836e42edd644b362db311147dd5aa8d5b82ba7a032f5269", size = 10924131, upload-time = "2026-05-05T13:54:39.329Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/78/29/ac41e16097af67066d97a7d5775c5d8e7efc5d0284f6b0a159e07b9adb92/django-6.0.6.tar.gz", hash = "sha256:ad03916ba59523d781ae5c3f631960c23d69a9d9c43cecda52fc23b47e953713", size = 10905525, upload-time = "2026-06-03T13:02:46.503Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/94/5b/1328f8b84fce040c404f76822bf8c57d254e368e8cbd8bd67ec2b26d75f5/django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0", size = 8368680, upload-time = "2026-05-05T13:54:33.532Z" },
|
{ url = "https://files.pythonhosted.org/packages/eb/50/23f9dc45483419a3cc2085b498b25adfbf10642b2941c73e6d2dfaffc9ab/django-6.0.6-py3-none-any.whl", hash = "sha256:25148b1194c47c2e685e5f5e9c5d59c78b075dfd282cb9618861ba6c1708f4d2", size = 8373354, upload-time = "2026-06-03T13:02:41.72Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1147,11 +1147,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.29.0"
|
version = "3.29.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1f/f9/f38573ed5844586db374d085911740a501ccfa373b455fc9413f09f85237/filelock-3.29.1.tar.gz", hash = "sha256:d97e6b1b9757569626c58caa07dc4beb1613f4a2938b1e8cc81afca398906c9e", size = 59335, upload-time = "2026-06-03T15:19:04.053Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" },
|
{ url = "https://files.pythonhosted.org/packages/4c/a0/614c5fe402fd88951df45f4dda2fa3b4e17a99ecd92340771929169b3b95/filelock-3.29.1-py3-none-any.whl", hash = "sha256:85199dfd706869641b72b2e8955d5416a4b2b7dc4b0e8e6d97b4cc1299a6983b", size = 40750, upload-time = "2026-06-03T15:19:02.959Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1305,7 +1305,7 @@ requests = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-genai"
|
name = "google-genai"
|
||||||
version = "2.7.0"
|
version = "2.8.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
@@ -1319,9 +1319,9 @@ dependencies = [
|
|||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
{ name = "websockets" },
|
{ name = "websockets" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/7b/6eb3b3d545b6bb4c374acba1ccf91b0f33b605e551536a6243cfcef2f07f/google_genai-2.7.0.tar.gz", hash = "sha256:3c6f32f5ced9877ededd1b384b5e5b7f09c20046ec3390b662b16d8cd1882ac5", size = 555853, upload-time = "2026-05-28T15:39:24.58Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/5b/52/0244e310812f3063d09d60b30ae29ab7df9343bd005744cd5eeaa6ba39b4/google_genai-2.8.0.tar.gz", hash = "sha256:37a9b3cb127d763e7f4ca47452ae3562c87728773bd1b149f7b559c239da2bc1", size = 564955, upload-time = "2026-06-03T22:55:38.397Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/3c/dd/7a8be39e9d698e80e9db796514efbc6083dbd787bdb9a101e8ba47248e5e/google_genai-2.7.0-py3-none-any.whl", hash = "sha256:21cac381e09a869151706aba797b6a4f96cfe92c484e13204d092caee7ff11cb", size = 822545, upload-time = "2026-05-28T15:39:22.907Z" },
|
{ url = "https://files.pythonhosted.org/packages/e2/de/747ad1aa49e902da9a4699081c282a1ed8ceed3b4d295fd99a6d286e09e4/google_genai-2.8.0-py3-none-any.whl", hash = "sha256:4da0a223a100f4b37f609a68b835e3326ab0fa313314dc0fd9d34e76ee293844", size = 832497, upload-time = "2026-06-03T22:55:36.598Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1381,18 +1381,18 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hf-xet"
|
name = "hf-xet"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/74/d8/5c06fc76461418326a7decf8367480c35be11a41fd938633929c60a9ec6b/hf_xet-1.5.0.tar.gz", hash = "sha256:e0fb0a34d9f406eed88233e829a67ec016bec5af19e480eac65a233ea289a948", size = 837196, upload-time = "2026-05-06T06:18:15.583Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/4b/2d/57fd21d84d93efb4bd0b962383790e19dd1bc053501b4264c97903b4e83e/hf_xet-1.5.1.tar.gz", hash = "sha256:51ef4500dab3764b41135ee1381a4b62ce56fc54d4c92b719b59e597d6df5bf6", size = 876636, upload-time = "2026-06-08T23:02:53.897Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/3d/fb/69ff198a82cae7eb1a69fb84d93b3a3e4816564d76817fe541ddc96874eb/hf_xet-1.5.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dad0dc84e941b8ba3c860659fe1fdc35c049d47cce293f003287757e971a8f56", size = 4030814, upload-time = "2026-05-06T06:17:57.933Z" },
|
{ url = "https://files.pythonhosted.org/packages/7a/d8/5e54cf37434759d1f4f2ba9b66077ff9d4c4e1f37b6bd7975da5c40d94ab/hf_xet-1.5.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6abd35c3221eff63836618ddfb954dcf84798603f71d8e33e3ed7b04acfdbe6e", size = 4077794, upload-time = "2026-06-08T23:02:40.656Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/9b/ff/edcc2b40162bef3ff78e14ab637e5f3b89243d6aee72f5949d3bb6a5af83/hf_xet-1.5.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fd6e5a9b0fdac4ed03ed45ef79254a655b1aaab514a02202617fbf643f5fdf7a", size = 3798444, upload-time = "2026-05-06T06:17:55.79Z" },
|
{ url = "https://files.pythonhosted.org/packages/35/94/4b2ecfbad8f8b04701a23aefb62f540b9137d058b7e1dbef16a32676f0e9/hf_xet-1.5.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:94e761bbd266bf4c03cee73753916062665ce8365aa40ed321f45afcb934b41e", size = 3845354, upload-time = "2026-06-08T23:02:42.702Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/49/4d/103f76b04310e5e57656696cc184690d20c466af0bca3ca88f8c8ea5d4f3/hf_xet-1.5.0-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3531b1823a0e6d77d80f9ed15ca0e00f0d115094f8ac033d5cae88f4564cc949", size = 4465986, upload-time = "2026-05-06T06:17:44.886Z" },
|
{ url = "https://files.pythonhosted.org/packages/de/cc/f99f4bc7295023d7bd9ebbfd51f75cc530ca262c1227666268b8208f4b77/hf_xet-1.5.1-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:892e3a3a3aecc12aded8b93cf4f9cd059282c7de0732f7d55026f3abdf474350", size = 4514864, upload-time = "2026-06-08T23:02:44.497Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c4/a2/546f47f464737b3edbab6f8ddb57f2599b93d2cbb66f06abb475ccb48651/hf_xet-1.5.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9a0ee58cd18d5ea799f7ed11290bbccbe56bdd8b1d97ca74b9cc49a3945d7a3b", size = 4259865, upload-time = "2026-05-06T06:17:42.639Z" },
|
{ url = "https://files.pythonhosted.org/packages/cd/6e/21f7e5a2381278bd3b7b7a5a4d90038518bb6308a0c1daf5d9f8268bb178/hf_xet-1.5.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a93df2039190502835b1db8cd7e178b0b7b889fe9ab51299d5ced26e0dd879a4", size = 4303784, upload-time = "2026-06-08T23:02:46.203Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/95/7f/1be593c1f28613be2e196473481cd81bfc5910795e30a34e8f744f6cac4f/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e60df5a42e9bed8628b6416af2cba4cba57ae9f02de226a06b020d98e1aab18", size = 4459835, upload-time = "2026-05-06T06:18:08.026Z" },
|
{ url = "https://files.pythonhosted.org/packages/35/0e/f992bb6927ac1cb30ef74e62268f551f338bc32b2191f7c96a44c6f7283e/hf_xet-1.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0c97106032ef70467b4f6bc2d0ccc266d7613ee076afc56516c502f87ce1c4a6", size = 4500703, upload-time = "2026-06-08T23:02:47.628Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/aa/b2/703569fc881f3284487e68cda7b42179978480da3c438042a6bbbb4a671c/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4b35549ce62601b84da4ff9b24d970032ace3d4430f52d91bcbb26c901d6c690", size = 4672414, upload-time = "2026-05-06T06:18:09.864Z" },
|
{ url = "https://files.pythonhosted.org/packages/fb/d1/90a498d05447980b977b1669246eeeeae4cfb0ea3e7a286eaba627f91bf9/hf_xet-1.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6208adb15d192b90e4c2ad2a27ed864359b2cb0f2494eb6d7c7f3699ac02e2bf", size = 4719498, upload-time = "2026-06-08T23:02:49.268Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/af/37/1b6def445c567286b50aa3b33828158e135b1be44938dde59f11382a500c/hf_xet-1.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:2806c7c17b4d23f8d88f7c4814f838c3b6150773fe339c20af23e1cfaf2797e4", size = 3977238, upload-time = "2026-05-06T06:18:23.621Z" },
|
{ url = "https://files.pythonhosted.org/packages/6d/b6/20f99cfe97cc663a711f7b33cc21d4793e51968e9a26125b4afcd77315ba/hf_xet-1.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:f7b3002f95d1c13e24bcb4537baa8f0eb3838957067c91bb4959bc004a6435f5", size = 4026419, upload-time = "2026-06-08T23:02:50.829Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/62/94/3b66b148778ee100dcfd69c2ca22b57b41b44d3063ceec934f209e9184ce/hf_xet-1.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:b6c9df403040248c76d808d3e047d64db2d923bae593eb244c41e425cf6cd7be", size = 3806916, upload-time = "2026-05-06T06:18:21.7Z" },
|
{ url = "https://files.pythonhosted.org/packages/f9/fa/77453694888f03e5a8c8852d1514a0894d8e81c622d39edbaf308ea0dcf4/hf_xet-1.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:93d090b57b211133f6c0dab0205ef5cb6d89162979ba75a74845045cc3063b8e", size = 3855178, upload-time = "2026-06-08T23:02:52.452Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1469,11 +1469,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.17"
|
version = "3.18"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1698,16 +1698,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain"
|
name = "langchain"
|
||||||
version = "1.3.2"
|
version = "1.3.4"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "langchain-core" },
|
{ name = "langchain-core" },
|
||||||
{ name = "langgraph" },
|
{ name = "langgraph" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d5/d0/c7f9d3d26c0e3f8bb146c6d707ee0fc1d30d8da65a59626e8a580085e929/langchain-1.3.2.tar.gz", hash = "sha256:ffd5f204a46b5fa1a38bf89ba3b45ca0902c02d18fa7d2a2eaeaeb1f5bf19d0a", size = 600598, upload-time = "2026-05-26T18:17:57.715Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/36/3f/034eb6cbef90bfccc89b7f8ed0c1d853dc9cb0bea17c7a269534c647ba3a/langchain-1.3.4.tar.gz", hash = "sha256:d6e0654c22848925534f5c0a706f9be481bb09a619ec60a738fbd1e5502e457a", size = 606617, upload-time = "2026-06-02T20:04:49.411Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/82/a54edcd1c48163de5642eb10fa2cb58b13a8889c659964f63f0306b58b1e/langchain-1.3.2-py3-none-any.whl", hash = "sha256:900f6b3f4ee08b9ba3cdbe667dbf42525bd6f66a4a07a7f1db26262673e41ed6", size = 121225, upload-time = "2026-05-26T18:17:56.075Z" },
|
{ url = "https://files.pythonhosted.org/packages/a5/29/9ffe99c7dc4891a0215ec59c423bea320f943c08a231bc5bae392a438a83/langchain-1.3.4-py3-none-any.whl", hash = "sha256:e51b05ab23d056bc6bf2d97d8c694fb92d6d5765126fef74565d007c27581672", size = 125286, upload-time = "2026-06-02T20:04:48.13Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1766,7 +1766,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-core"
|
name = "langchain-core"
|
||||||
version = "1.4.0"
|
version = "1.4.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jsonpatch" },
|
{ name = "jsonpatch" },
|
||||||
@@ -1779,9 +1779,9 @@ dependencies = [
|
|||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
{ name = "uuid-utils" },
|
{ name = "uuid-utils" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/59/de/679a53472c25860837e32c0442c962fa86e95317a36460e2c9d5c91b17c2/langchain_core-1.4.0.tar.gz", hash = "sha256:1dc341eed802ed9c117c0df3923c991e5e9e226571e5725c194eeb5bd93d1a7f", size = 920260, upload-time = "2026-05-11T18:42:35.919Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/de/13/446580dc9f26e4e524d57f727a9007b4c2484decd2c00269b7fd4f51326d/langchain_core-1.4.2.tar.gz", hash = "sha256:242abe763db71de05fe0d7ecff03f9cc6022fbceba8be15902fb89e35b7292f9", size = 935103, upload-time = "2026-06-08T18:19:41.611Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/0f/1a/86c38c27b81913a1c6c12448cab55defb5a1097c7dc9a4cea83f55477a2d/langchain_core-1.4.0-py3-none-any.whl", hash = "sha256:23cbbdb46e38ddd1dd5247e6167e96013eae74bea4c5949c550809970a9e565c", size = 548120, upload-time = "2026-05-11T18:42:33.992Z" },
|
{ url = "https://files.pythonhosted.org/packages/d0/2c/92a6a6f5af07f1c347021d81aea307d405ed9d34a4f8ea6b78cfa3c3b189/langchain_core-1.4.2-py3-none-any.whl", hash = "sha256:a2906d339514e02a46d6c0888021dd2651ed5acc661a1f546fe33e1453adfcb9", size = 550103, upload-time = "2026-06-08T18:19:40.197Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1861,7 +1861,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langgraph"
|
name = "langgraph"
|
||||||
version = "1.2.2"
|
version = "1.2.4"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "langchain-core" },
|
{ name = "langchain-core" },
|
||||||
@@ -1871,9 +1871,9 @@ dependencies = [
|
|||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "xxhash" },
|
{ name = "xxhash" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e6/5a/ffc12434ee8aecab830d58b4d204ddea45073eae7639c963310f671a5bf5/langgraph-1.2.2.tar.gz", hash = "sha256:f54a98458976b3ff0774683867df125fb52d8dbedeb2441d0b0656a51331cee5", size = 695730, upload-time = "2026-05-26T18:07:28.49Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1c/43/dac5a2621c1e57f8eb7f0703f6f6fe34a5caf62f8f0fb4d2bb395bb454ea/langgraph-1.2.4.tar.gz", hash = "sha256:5df076973a2d23efb13eceb279d1e5b46feebcbbeded0a86a2ef669abd9e4399", size = 720374, upload-time = "2026-06-02T17:07:37.347Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/42/9b/b08d578bba73e25351152dfd3d6d21e81210a5fff1b6f26e56f33197c8f5/langgraph-1.2.2-py3-none-any.whl", hash = "sha256:0a851bf4ba5939c5474a2fd57e6b439b5315283e254e42943bd392c2d71a5e03", size = 236376, upload-time = "2026-05-26T18:07:26.577Z" },
|
{ url = "https://files.pythonhosted.org/packages/48/9e/31ca236104966d7bb14ea9e93cfd73350aea8c41008ddf057b65794ed10d/langgraph-1.2.4-py3-none-any.whl", hash = "sha256:ffe3e1e31dce28907640f82525858470f293506d2b272d07ea3b3ce97974b067", size = 245402, upload-time = "2026-06-02T17:07:35.977Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1971,20 +1971,23 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langgraph-sdk"
|
name = "langgraph-sdk"
|
||||||
version = "0.3.15"
|
version = "0.4.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
|
{ name = "langchain-core" },
|
||||||
|
{ name = "langchain-protocol" },
|
||||||
{ name = "orjson" },
|
{ name = "orjson" },
|
||||||
|
{ name = "websockets" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/66/af/cdd4d6f3c05b3c1112ed3f12ef830faf15951b21d22cbc622a4becbbe25c/langgraph_sdk-0.3.15.tar.gz", hash = "sha256:29e805003d2c6e296823dd71992610976fd0428cefaa8b3304fd91f2247037de", size = 201924, upload-time = "2026-05-22T16:54:27.678Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b4/2b/bd8ac26d4e97f6df88ef05ce5b6a38945a3903e1025d926f4752aa88aa97/langgraph_sdk-0.4.2.tar.gz", hash = "sha256:b88f0f5f6328ac0680d6790614a905b2bcfa257f2276dba4e38f0e86db0aa738", size = 348327, upload-time = "2026-06-01T17:51:19.856Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/be/a5/0196d9c05749c25bc198e4909d68c998bc3120297e14944921baf2f4c384/langgraph_sdk-0.3.15-py3-none-any.whl", hash = "sha256:3838773acf7456d158165385d49f48f1e856f28b56ccd99ea139a8f27004815d", size = 98166, upload-time = "2026-05-22T16:54:26.013Z" },
|
{ url = "https://files.pythonhosted.org/packages/a0/05/aac507337cceae773c2cc9ab91eb6301963af7aeeb55b4217a00e15aff17/langgraph_sdk-0.4.2-py3-none-any.whl", hash = "sha256:75fa5096c1177ce39c847096a8fe3745ffd480ddb412995f836e9f5f884c43dd", size = 160521, upload-time = "2026-06-01T17:51:18.849Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langsmith"
|
name = "langsmith"
|
||||||
version = "0.8.8"
|
version = "0.8.11"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
@@ -1998,9 +2001,9 @@ dependencies = [
|
|||||||
{ name = "xxhash" },
|
{ name = "xxhash" },
|
||||||
{ name = "zstandard" },
|
{ name = "zstandard" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2f/93/28df12b3b3c776077983b92f1299c623592b5999695af2a755fb90ff048b/langsmith-0.8.8.tar.gz", hash = "sha256:9d00e54f54d833c1914003527ff03ad0364741034330da72f0adbeaba852b6cf", size = 4468035, upload-time = "2026-05-31T22:14:57.698Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/ea/0d/082410ece26ff9f3ed4f87b014a8675be47cbd7d65f06b922045dfc21c47/langsmith-0.8.11.tar.gz", hash = "sha256:d9b3496f8f7ca63f4f2d1dfd368afd6c527923fff2ce4026c82ce85f37db3965", size = 4495842, upload-time = "2026-06-08T22:54:44.395Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/8d/71/94a8f2b573278a0b0b7dfd37663c0ddd36867f9e2bba69addd183de0cd56/langsmith-0.8.8-py3-none-any.whl", hash = "sha256:9d60d724c0d187c036e184b3ffdf9fa5c6822aa0bb88144a5fb898e79be645af", size = 402712, upload-time = "2026-05-31T22:14:55.908Z" },
|
{ url = "https://files.pythonhosted.org/packages/b4/65/f9c9dc19b21a9076286fafdb0ab732c9019ddf71aa7e7d720a830a98fe2a/langsmith-0.8.11-py3-none-any.whl", hash = "sha256:08aa5e84b00703ecc11dbeafda78d84b92da4e8c6114e0be9b59df9e71afc59b", size = 478985, upload-time = "2026-06-08T22:54:42.349Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2467,7 +2470,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openai"
|
name = "openai"
|
||||||
version = "2.40.0"
|
version = "2.41.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
@@ -2479,9 +2482,9 @@ dependencies = [
|
|||||||
{ name = "tqdm" },
|
{ name = "tqdm" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/9f/136562ec6c3b1a50fe06eb0bb34ed21f0d7426ec0140e5cc43ac785b69a5/openai-2.40.0.tar.gz", hash = "sha256:9a756f91f274a24ad6026cbcb2042fd356c8d4a10e8f347b08d34465e585f7a2", size = 781177, upload-time = "2026-06-01T21:48:23.878Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/3c/a6/5815fe2e2aca74b36c650d1bd43b69827cee568073d0d2d9b6fc5aaac80c/openai-2.41.0.tar.gz", hash = "sha256:db5c362acd6604b84f076abbefa66826ea4b46ecba2954ed866e6a149a1352c0", size = 783525, upload-time = "2026-06-03T22:39:40.719Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f6/46/180e14be801a75bc13f234cb1b594b232adeb9c84e60a9ab1832e8333591/openai-2.40.0-py3-none-any.whl", hash = "sha256:2b205637ff214477f9ce9ab035e9f494db0e3fa8f1e599008953735fbf6ff1ff", size = 1350935, upload-time = "2026-06-01T21:48:21.462Z" },
|
{ url = "https://files.pythonhosted.org/packages/be/51/d82bb424e8aa372190c5233253a2ceb399a778747d18b42cff487411e663/openai-2.41.0-py3-none-any.whl", hash = "sha256:20cc7952e8501c7e5773dd2ef7be437bae9cb549044902e1041a83a54516e375", size = 1353378, upload-time = "2026-06-03T22:39:38.964Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3136,11 +3139,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-multipart"
|
name = "python-multipart"
|
||||||
version = "0.0.30"
|
version = "0.0.32"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/82/c8cd43a6e0719bf5a3b034f6726dd701f75829c08944c83d4b95d02ed0e8/python_multipart-0.0.30.tar.gz", hash = "sha256:0edfe0475c1f46ddd3ff7785a626f6118af32bdcf359bb21260367313bb32118", size = 46316, upload-time = "2026-05-31T19:24:55.198Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/1c/fd/0318007beb234790993d3ec5afd051d1dbceb733e81e3afe2b981ece3f37/python_multipart-0.0.30-py3-none-any.whl", hash = "sha256:830964def8c90607ac5daa00514e3987815865713ade8d20febc9177ac0c3c5b", size = 29730, upload-time = "2026-05-31T19:24:53.814Z" },
|
{ url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3306,16 +3309,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rich-toolkit"
|
name = "rich-toolkit"
|
||||||
version = "0.19.10"
|
version = "0.20.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
{ name = "rich" },
|
{ name = "rich" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/fa/02/32217f3657ae91a0ea7cf1d74ade78f44352f830d00c468f753ddb3d4980/rich_toolkit-0.19.10.tar.gz", hash = "sha256:dc2e8c515ef9fbb4894e62bd41a2d2960dd7c2f505b5084894604d5ccfee3f09", size = 198167, upload-time = "2026-05-21T10:11:42.397Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/29/63/3e427c62f1992945c997d4ec31e2fcb37d26aadbe5aa44ae5b29f7f64d26/rich_toolkit-0.20.1.tar.gz", hash = "sha256:c7336ae281f435c785acecaedc4b71d4b663dc73d9c8079fea96372527e822a4", size = 203473, upload-time = "2026-06-05T08:56:57.679Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/35/84/a005adcb4d1e6846ba3d62768090c3b943e3f6d8dc5c47af64f33584c4a7/rich_toolkit-0.19.10-py3-none-any.whl", hash = "sha256:93a41f67a09aefe90379f1729495c2fee9ccbcc8cfda48e2ca2ae54a995e32b1", size = 33907, upload-time = "2026-05-21T10:11:43.578Z" },
|
{ url = "https://files.pythonhosted.org/packages/00/88/309f07d08155da2ba1d5ceb42d270fb42fbe34a807684543e3ffc10fe713/rich_toolkit-0.20.1-py3-none-any.whl", hash = "sha256:2a6d5f8e15759b9eba5a9ee63da10b275359ead20e5a0fc92bd5b4dbae8ce4bf", size = 35525, upload-time = "2026-06-05T08:56:58.586Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3439,15 +3442,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-sdk"
|
name = "sentry-sdk"
|
||||||
version = "2.61.1"
|
version = "2.62.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi" },
|
||||||
{ name = "urllib3" },
|
{ name = "urllib3" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/63/3b/4bc6b348bbd331daa14d4babe9f2b99bc854f4da41560eefb9488d78481d/sentry_sdk-2.61.1.tar.gz", hash = "sha256:9c6adccb3feefa9ba032c8d295ca477575c2f11896046a2b0ad686c47c4af555", size = 459429, upload-time = "2026-06-01T07:24:18.875Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f6/5d/a343201726150e05f2036eeb6e493e2e2f8bf8a66f5aa70f2f4ac96f9ca3/sentry_sdk-2.62.0.tar.gz", hash = "sha256:3c870b9f50d9fd15b58c817dbde1c7cfaa9fe3f05df0a4c6edd5571cb82f5491", size = 463986, upload-time = "2026-06-08T13:23:49.223Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/df/54/c9218db183846e08efaf68534889ef42e499dde432778881104a42f7071b/sentry_sdk-2.61.1-py3-none-any.whl", hash = "sha256:fa36eaf4b8ad708f718500d4bdcc1532637526a22beb874d88cbc0a46458b5ae", size = 483735, upload-time = "2026-06-01T07:24:17.027Z" },
|
{ url = "https://files.pythonhosted.org/packages/3d/07/05440381627877aae223fd68f330df9b9fc6641d08bf65328b55235617a2/sentry_sdk-2.62.0-py3-none-any.whl", hash = "sha256:27f61d13a86c3c1648dec666dd5a64f79772dd6a84b446f11866601ecab24f6f", size = 490586, upload-time = "2026-06-08T13:23:47.486Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3705,14 +3708,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tqdm"
|
name = "tqdm"
|
||||||
version = "4.67.3"
|
version = "4.68.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/06/b3/36c8ecf72e8925200671613332db156d84b99b3aee742a41c1938ebb0808/tqdm-4.68.1.tar.gz", hash = "sha256:fc163d96b287bd031e1aa24421ce4411b25559bd0a1be4fe649bdaa4d2c02bf5", size = 171236, upload-time = "2026-06-05T17:23:15.267Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
|
{ url = "https://files.pythonhosted.org/packages/47/aa/218a0eb34de1f753c83e4d0d1c8e7c4cef27f20dcb8342e024f63a80dc86/tqdm-4.68.1-py3-none-any.whl", hash = "sha256:fea4a90e4023f764914569f7802a297277c5ab1a66be5144143e142e1a4031d8", size = 78354, upload-time = "2026-06-05T17:23:13.654Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3738,6 +3741,7 @@ dependencies = [
|
|||||||
{ name = "langchain-community" },
|
{ name = "langchain-community" },
|
||||||
{ name = "langchain-qwq" },
|
{ name = "langchain-qwq" },
|
||||||
{ name = "langgraph" },
|
{ name = "langgraph" },
|
||||||
|
{ name = "langgraph-api" },
|
||||||
{ name = "langgraph-cli", extra = ["inmem"] },
|
{ name = "langgraph-cli", extra = ["inmem"] },
|
||||||
{ name = "load" },
|
{ name = "load" },
|
||||||
{ name = "load-dotenv" },
|
{ name = "load-dotenv" },
|
||||||
@@ -3794,6 +3798,7 @@ requires-dist = [
|
|||||||
{ name = "langchain-community", specifier = ">=0.4.1" },
|
{ name = "langchain-community", specifier = ">=0.4.1" },
|
||||||
{ 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-cli", extras = ["inmem", "redis"], specifier = "<=0.4.26" },
|
{ name = "langgraph-cli", extras = ["inmem", "redis"], specifier = "<=0.4.26" },
|
||||||
{ name = "load", specifier = ">=1.0.14" },
|
{ name = "load", specifier = ">=1.0.14" },
|
||||||
{ name = "load-dotenv", specifier = ">=0.1.0" },
|
{ name = "load-dotenv", specifier = ">=0.1.0" },
|
||||||
@@ -3877,7 +3882,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typer"
|
name = "typer"
|
||||||
version = "0.26.5"
|
version = "0.26.7"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "annotated-doc" },
|
{ name = "annotated-doc" },
|
||||||
@@ -3885,9 +3890,9 @@ dependencies = [
|
|||||||
{ name = "rich" },
|
{ name = "rich" },
|
||||||
{ name = "shellingham" },
|
{ name = "shellingham" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/1a/2cf40b65b1d9c254fe5814bb0519f9b8f2ac38059df0810f9b866300c04a/typer-0.26.5.tar.gz", hash = "sha256:9b9b39e35c3afc9e1e51a06f21155246e457c0911279b09b35d8210ca74b935c", size = 201494, upload-time = "2026-06-01T14:42:49.744Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/5e/ed/ef06584ccdd5c410df0837951ecd7e15d9a6144ea1bd4c73cecab1a89891/typer-0.26.7.tar.gz", hash = "sha256:e314a34c617e419c091b2830dda3ea1f257134ff593061a8f5b9717ab8dddb3a", size = 201709, upload-time = "2026-06-03T07:18:06.843Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ec/d6/baac76fc04a6532883de3d8722c7f921dae94d10965e7ffba9e38e42a251/typer-0.26.5-py3-none-any.whl", hash = "sha256:4bfd901d564e41608920134aa5d4481200f4ba76d98e982d9f9d32dcb7b84da0", size = 122451, upload-time = "2026-06-01T14:42:51.021Z" },
|
{ url = "https://files.pythonhosted.org/packages/24/25/2201973529af2c954de0bb725323c3aaed6d7f0ceee8f550dec9185df013/typer-0.26.7-py3-none-any.whl", hash = "sha256:5c87cfbc5d34491c5346ebf49c23e18d56ccb863268d3a8d592b26087c2f5e58", size = 122456, upload-time = "2026-06-03T07:18:05.732Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3974,15 +3979,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.48.0"
|
version = "0.49.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
{ name = "h11" },
|
{ name = "h11" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" },
|
{ url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
@@ -4058,11 +4063,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wcwidth"
|
name = "wcwidth"
|
||||||
version = "0.7.0"
|
version = "0.8.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132, upload-time = "2026-05-02T16:04:12.653Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/49/b4/51fe890511f0f242d07cb1ebe6a5b6db417262b9d2568b460347c57d95cc/wcwidth-0.8.1.tar.gz", hash = "sha256:faf5b4a5366a72dc49cad48cdf21f52bdf63bdda995178e483ba247ff79089b9", size = 1466072, upload-time = "2026-06-08T05:57:23.146Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825, upload-time = "2026-05-02T16:04:11.033Z" },
|
{ url = "https://files.pythonhosted.org/packages/bd/6e/95b0e537de1f4d4301f76f944642c6da50d1511cc7b3d64dc418a66c7509/wcwidth-0.8.1-py3-none-any.whl", hash = "sha256:f453740b1e4a4f3291faa37944c555d71056c4da08d59809b307ef4feba695c8", size = 323092, upload-time = "2026-06-08T05:57:21.413Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4076,20 +4081,22 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "websockets"
|
name = "websockets"
|
||||||
version = "16.0"
|
version = "15.0.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" },
|
{ url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" },
|
{ url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" },
|
{ url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" },
|
{ url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" },
|
{ url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" },
|
{ url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" },
|
{ url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" },
|
{ url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" },
|
{ url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
Reference in New Issue
Block a user