Files
AIDC-AI-ComfyUI-Copilot/service/conversation_service.py
T
2025-02-14 18:15:30 +08:00

244 lines
7.8 KiB
Python

# Copyright (C) 2025 AIDC-AI
# Licensed under the MIT License.
import json
import os
import asyncio
import time
from typing import Optional, Dict, Any, TypedDict, List, Union
import server
from aiohttp import web
import aiohttp
import base64
# 使用内存字典存储会话消息
session_messages = {}
# 在文件开头添加
STATIC_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "public")
# Define types using TypedDict
class Node(TypedDict):
name: str # 节点名称
description: str # 节点描述
image: str # 节点图片url,可为空
github_url: str # 节点github地址
from_index: int # 节点在列表中的位置
to_index: int # 节点在列表中的位置
class NodeInfo(TypedDict):
existing_nodes: List[Node] # 已安装的节点
missing_nodes: List[Node] # 未安装的节点
class Workflow(TypedDict, total=False):
id: Optional[int] # 工作流id
name: Optional[str] # 工作流名称
description: Optional[str] # 工作流描述
image: Optional[str] # 工作流图片
workflow: Optional[str] # 工作流
class ExtItem(TypedDict):
type: str # 扩展类型
data: Union[dict, list] # 扩展数据
class ChatResponse(TypedDict):
session_id: str # 会话id
text: Optional[str] # 返回文本
finished: bool # 是否结束
type: str # 返回的类型
format: str # 返回的格式
ext: Optional[List[ExtItem]] # 扩展信息
def get_workflow_templates():
templates = []
workflows_dir = os.path.join(STATIC_DIR, "workflows")
for filename in os.listdir(workflows_dir):
if filename.endswith('.json'):
with open(os.path.join(workflows_dir, filename), 'r') as f:
template = json.load(f)
templates.append(template)
return templates
@server.PromptServer.instance.routes.get("/workspace/fetch_messages_by_id")
async def fetch_messages(request):
session_id = request.query.get('session_id')
data = await asyncio.to_thread(fetch_messages_sync, session_id)
return web.json_response(data)
def fetch_messages_sync(session_id):
print("fetch_messages: ", session_id)
return session_messages.get(session_id, [])
@server.PromptServer.instance.routes.post("/workspace/workflow_gen")
async def workflow_gen(request):
print("Received workflow_gen request")
req_json = await request.json()
print("Request JSON:", req_json)
response = web.StreamResponse(
status=200,
reason='OK',
headers={
'Content-Type': 'application/json',
'X-Content-Type-Options': 'nosniff'
}
)
await response.prepare(request)
session_id = req_json.get('session_id')
user_message = req_json.get('message')
# Create user message
user_msg = {
"id": str(len(session_messages.get(session_id, []))),
"content": user_message,
"role": "user"
}
if "workflow" in user_message.lower():
workflow = {
"name": "basic_image_gen",
"description": "Create a basic image generation workflow",
"image": "https://placehold.co/600x400",
"workflow": """{ ... }""" # Your workflow JSON here
}
chat_response = ChatResponse(
session_id=session_id,
text="",
finished=False,
type="workflow_option",
format="text",
ext=[{"type": "workflows", "data": [workflow]}]
)
await response.write(json.dumps(chat_response).encode() + b"\n")
message = "Let me help you choose a workflow. Here are some options available:"
accumulated = ""
for char in message:
accumulated += char
chat_response["text"] = accumulated
await response.write(json.dumps(chat_response).encode() + b"\n")
await asyncio.sleep(0.01)
chat_response["finished"] = True
chat_response["text"] = message
await response.write(json.dumps(chat_response).encode() + b"\n")
elif "recommend" in user_message.lower():
existing_nodes = [
{
"name": "LoraLoader",
"description": "Load LoRA weights for conditioning.",
"image": "",
"github_url": "https://github.com/CompVis/taming-transformers",
"from_index": 0,
"to_index": 0
},
{
"name": "KSampler",
"description": "Generate images using K-diffusion sampling.",
"image": "",
"github_url": "https://github.com/CompVis/taming-transformers",
"from_index": 0,
"to_index": 0
}
]
missing_nodes = [
{
"name": "CLIPTextEncode",
"description": "Encode text prompts for conditioning.",
"image": "",
"github_url": "https://github.com/CompVis/clip-interrogator",
"from_index": 0,
"to_index": 0
}
]
node_info = {
"existing_nodes": existing_nodes,
"missing_nodes": missing_nodes
}
chat_response = ChatResponse(
session_id=session_id,
text="",
finished=False,
type="downstream_node_recommend",
format="text",
ext=[{"type": "node_info", "data": node_info}]
)
await response.write(json.dumps(chat_response).encode() + b"\n")
message = "Here are some recommended nodes:"
accumulated = ""
for char in message:
accumulated += char
chat_response["text"] = accumulated
await response.write(json.dumps(chat_response).encode() + b"\n")
await asyncio.sleep(0.01)
chat_response["finished"] = True
chat_response["text"] = message
await response.write(json.dumps(chat_response).encode() + b"\n")
else:
chat_response = ChatResponse(
session_id=session_id,
text="",
finished=False,
type="message",
format="text",
ext=[{"type": "guides", "data": ["Create a workflow", "Search for nodes", "Get node recommendations"]}]
)
await response.write(json.dumps(chat_response).encode() + b"\n")
message = "I can help you with workflows, nodes, and more. Try asking about:"
accumulated = ""
for char in message:
accumulated += char
chat_response["text"] = accumulated
await response.write(json.dumps(chat_response).encode() + b"\n")
await asyncio.sleep(0.01)
chat_response["finished"] = True
chat_response["text"] = message
await response.write(json.dumps(chat_response).encode() + b"\n")
if session_id not in session_messages:
session_messages[session_id] = []
session_messages[session_id].extend([user_msg])
await response.write_eof()
return response
async def upload_to_oss(file_data: bytes, filename: str) -> str:
# Implement your OSS upload logic here
# Return the URL of the uploaded file
pass
@server.PromptServer.instance.routes.post("/api/chat/invoke")
async def invoke_chat(request):
data = await request.json()
session_id = data['session_id']
prompt = data['prompt']
images = data.get('images', [])
image_urls = []
for image in images:
# 从base64解码图片数据
image_data = base64.b64decode(image['data'].split(',')[1])
# 上传到OSS
url = await upload_to_oss(image_data, image['filename'])
image_urls.append(url)
# 使用image_urls进行后续处理...