feat: 开放所有apikey
This commit is contained in:
@@ -239,6 +239,9 @@ async def invoke_chat(request):
|
||||
"workflow_checkpoint_id": workflow_checkpoint_id,
|
||||
"openai_api_key": request.headers.get('Openai-Api-Key'),
|
||||
"openai_base_url": request.headers.get('Openai-Base-Url'),
|
||||
# Workflow LLM settings (optional, used by tools/agents that need a different LLM)
|
||||
"workflow_llm_api_key": request.headers.get('Workflow-LLM-Api-Key'),
|
||||
"workflow_llm_base_url": request.headers.get('Workflow-LLM-Base-Url'),
|
||||
"model_select": next((x['data'][0] for x in ext if x['type'] == 'model_select' and x.get('data')), None)
|
||||
}
|
||||
|
||||
@@ -504,7 +507,10 @@ async def invoke_debug(request):
|
||||
"session_id": session_id,
|
||||
"model": "gemini-2.5-flash", # Default model for debug agents
|
||||
"openai_api_key": request.headers.get('Openai-Api-Key'),
|
||||
"openai_base_url": request.headers.get('Openai-Base-Url', 'https://api.openai.com/v1'),
|
||||
"openai_base_url": request.headers.get('Openai-Base-Url'),
|
||||
# Workflow LLM settings (optional)
|
||||
"workflow_llm_api_key": request.headers.get('Workflow-LLM-Api-Key'),
|
||||
"workflow_llm_base_url": request.headers.get('Workflow-LLM-Base-Url'),
|
||||
}
|
||||
|
||||
# 获取当前语言
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'''
|
||||
Debug Agent for ComfyUI Workflow Error Analysis
|
||||
'''
|
||||
from ..utils.key_utils import workflow_config_adapt
|
||||
from ..agent_factory import create_agent
|
||||
from agents.items import ItemHelpers
|
||||
from agents.run import Runner
|
||||
@@ -205,6 +206,7 @@ async def debug_workflow_errors(workflow_data: Dict[str, Any]):
|
||||
# Get session_id and config from request context
|
||||
session_id = get_session_id()
|
||||
config = get_config()
|
||||
workflow_config_adapt(config)
|
||||
|
||||
if not session_id:
|
||||
session_id = str(uuid.uuid4()) # Fallback if no context
|
||||
@@ -253,7 +255,8 @@ Start by validating the workflow to see its current state.""",
|
||||
model=WORKFLOW_MODEL_NAME,
|
||||
tools=[run_workflow, analyze_error_type, save_current_workflow],
|
||||
config={
|
||||
"max_tokens": 8192
|
||||
"max_tokens": 8192,
|
||||
**config
|
||||
}
|
||||
)
|
||||
|
||||
@@ -302,7 +305,8 @@ Start by validating the workflow to see its current state.""",
|
||||
tools=[get_current_workflow, get_node_info, update_workflow],
|
||||
handoffs=[agent],
|
||||
config={
|
||||
"max_tokens": 8192
|
||||
"max_tokens": 8192,
|
||||
**config
|
||||
}
|
||||
)
|
||||
|
||||
@@ -393,7 +397,8 @@ Start by validating the workflow to see its current state.""",
|
||||
get_current_workflow, get_node_info],
|
||||
handoffs=[agent],
|
||||
config={
|
||||
"max_tokens": 8192
|
||||
"max_tokens": 8192,
|
||||
**config
|
||||
}
|
||||
)
|
||||
|
||||
@@ -487,7 +492,8 @@ Start by validating the workflow to see its current state.""",
|
||||
suggest_model_download, update_workflow_parameter, get_current_workflow],
|
||||
handoffs=[agent],
|
||||
config={
|
||||
"max_tokens": 8192
|
||||
"max_tokens": 8192,
|
||||
**config
|
||||
}
|
||||
)
|
||||
|
||||
@@ -692,16 +698,9 @@ Start by validating the workflow to see its current state.""",
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"Error in debug_workflow_errors: {str(e)}")
|
||||
error_message = current_text + f"\n\n× Error occurred during debugging: {str(e)}\n\n"
|
||||
error_message = f"\n\n× Error occurred during debugging: {str(e)}\n\n"
|
||||
|
||||
# Include workflow_update ext if captured from tools before the error
|
||||
final_error_ext = None
|
||||
if 'workflow_update_ext' in locals() and workflow_update_ext:
|
||||
final_error_ext = [workflow_update_ext]
|
||||
log.info(f"-- Including latest workflow_update ext in error response")
|
||||
|
||||
ext_with_finished = {
|
||||
"data": final_error_ext,
|
||||
"finished": True
|
||||
}
|
||||
yield (error_message, ext_with_finished)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Author: ai-business-hql qingli.hql@alibaba-inc.com
|
||||
Date: 2025-07-24 17:10:23
|
||||
LastEditors: ai-business-hql ai.bussiness.hql@gmail.com
|
||||
LastEditTime: 2025-08-28 11:15:49
|
||||
LastEditTime: 2025-09-30 11:03:37
|
||||
FilePath: /comfyui_copilot/backend/service/workflow_rewrite_agent.py
|
||||
Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
'''
|
||||
@@ -14,11 +14,13 @@ import uuid
|
||||
import os
|
||||
from typing import Dict, Any
|
||||
|
||||
from ..utils.key_utils import workflow_config_adapt
|
||||
|
||||
from ..dao.expert_table import list_rewrite_experts_short, get_rewrite_expert_by_name_list
|
||||
|
||||
from ..agent_factory import create_agent
|
||||
from ..utils.globals import WORKFLOW_MODEL_NAME, get_language
|
||||
from ..utils.request_context import get_session_id
|
||||
from ..utils.request_context import get_config, get_session_id
|
||||
|
||||
from ..service.workflow_rewrite_tools import *
|
||||
|
||||
@@ -42,7 +44,9 @@ def create_workflow_rewrite_agent():
|
||||
|
||||
language = get_language()
|
||||
session_id = get_session_id() or "unknown_session"
|
||||
|
||||
config = get_config()
|
||||
workflow_config_adapt(config)
|
||||
|
||||
return create_agent(
|
||||
name="Workflow Rewrite Agent",
|
||||
model=WORKFLOW_MODEL_NAME,
|
||||
|
||||
@@ -11,11 +11,12 @@ import json
|
||||
from typing import Dict, Any, Optional
|
||||
import asyncio
|
||||
from pydantic import BaseModel
|
||||
from ..utils.key_utils import workflow_config_adapt
|
||||
from openai import OpenAI
|
||||
|
||||
from ..agent_factory import create_agent
|
||||
from ..utils.globals import WORKFLOW_MODEL_NAME, get_comfyui_copilot_api_key, LLM_DEFAULT_BASE_URL
|
||||
from ..utils.request_context import get_rewrite_context, RewriteContext
|
||||
from ..utils.request_context import get_config, get_rewrite_context, RewriteContext
|
||||
from ..utils.logger import log
|
||||
|
||||
|
||||
@@ -81,11 +82,12 @@ ComfyUI API格式工作流是一个JSON对象,其中:
|
||||
"workflow_data": "这里是完整的工作流JSON字符串"
|
||||
}
|
||||
"""
|
||||
|
||||
config = get_config()
|
||||
workflow_config_adapt(config)
|
||||
# 创建OpenAI客户端
|
||||
client = OpenAI(
|
||||
base_url = LLM_DEFAULT_BASE_URL,
|
||||
api_key = get_comfyui_copilot_api_key() or ""
|
||||
base_url = config.get("openai_base_url") or LLM_DEFAULT_BASE_URL,
|
||||
api_key = config.get("openai_api_key") or get_comfyui_copilot_api_key() or ""
|
||||
)
|
||||
|
||||
# 调用LLM
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
Author: ai-business-hql qingli.hql@alibaba-inc.com
|
||||
Date: 2025-08-08 17:14:52
|
||||
LastEditors: ai-business-hql ai.bussiness.hql@gmail.com
|
||||
<<<<<<< HEAD
|
||||
LastEditTime: 2025-08-25 17:41:14
|
||||
=======
|
||||
LastEditTime: 2025-08-29 11:25:52
|
||||
>>>>>>> mcp
|
||||
LastEditTime: 2025-09-30 10:18:44
|
||||
FilePath: /comfyui_copilot/backend/utils/globals.py
|
||||
Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
'''
|
||||
@@ -15,8 +11,16 @@ Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查
|
||||
Global utilities for managing application-wide state and configuration.
|
||||
"""
|
||||
|
||||
import os
|
||||
import threading
|
||||
from typing import Optional, Dict, Any
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load .env file if it exists
|
||||
env_path = Path(__file__).parent.parent.parent / '.env'
|
||||
if env_path.exists():
|
||||
load_dotenv(env_path)
|
||||
|
||||
class GlobalState:
|
||||
"""Thread-safe global state manager for application-wide configuration."""
|
||||
@@ -95,11 +99,11 @@ def set_comfyui_copilot_api_key(api_key: str) -> None:
|
||||
_global_state.set('comfyui_copilot_api_key', api_key)
|
||||
|
||||
|
||||
BACKEND_BASE_URL = "https://comfyui-copilot-server.onrender.com"
|
||||
BACKEND_BASE_URL = os.getenv("BACKEND_BASE_URL", "https://comfyui-copilot-server.onrender.com")
|
||||
LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1"
|
||||
WORKFLOW_MODEL_NAME = "us.anthropic.claude-sonnet-4-20250514-v1:0"
|
||||
WORKFLOW_MODEL_NAME = os.getenv("WORKFLOW_MODEL_NAME", "us.anthropic.claude-sonnet-4-20250514-v1:0")
|
||||
# WORKFLOW_MODEL_NAME = "gpt-5-2025-08-07-GlobalStandard"
|
||||
LLM_DEFAULT_BASE_URL = BACKEND_BASE_URL + "/v1"
|
||||
LLM_DEFAULT_BASE_URL = os.getenv("LLM_DEFAULT_BASE_URL", BACKEND_BASE_URL + "/v1")
|
||||
|
||||
|
||||
def is_lmstudio_url(base_url: str) -> bool:
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
def workflow_config_adapt(config: dict):
|
||||
if config:
|
||||
if config.get("workflow_llm_api_key"):
|
||||
config["openai_api_key"] = config.get("workflow_llm_api_key")
|
||||
config["workflow_llm_api_key"] = None
|
||||
if config.get("workflow_llm_base_url"):
|
||||
config["openai_base_url"] = config.get("workflow_llm_base_url")
|
||||
config["workflow_llm_base_url"] = None
|
||||
@@ -1,7 +1,7 @@
|
||||
function getImportPath(filename) {
|
||||
return `./${filename}`;
|
||||
}
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["copilot_web/workflowChat-C7at41Bc.js","copilot_web/vendor-markdown-B4Av9P7l.js","copilot_web/vendor-react-ByKzdfMq.js","copilot_web/message-components-VDKdm9Kg.js","copilot_web/input.js","copilot_web/assets/input-UcJyCCDl.css","copilot_web/fonts.css"].map(path => {
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["copilot_web/workflowChat-B4zoMsdC.js","copilot_web/vendor-markdown-B4Av9P7l.js","copilot_web/vendor-react-ByKzdfMq.js","copilot_web/message-components-C_zeUc_b.js","copilot_web/input.js","copilot_web/assets/input-UcJyCCDl.css","copilot_web/fonts.css"].map(path => {
|
||||
const apiBase = window.comfyAPI?.api?.api?.api_base;
|
||||
if (apiBase) {
|
||||
// 有 API base 时,使用完整路径
|
||||
@@ -11,4 +11,4 @@ function getImportPath(filename) {
|
||||
return `./${path}`;
|
||||
}
|
||||
}))))=>i.map(i=>d[i]);
|
||||
import{_ as a}from"./input.js";import{j as o}from"./vendor-markdown-B4Av9P7l.js";import{r as t,R as i}from"./vendor-react-ByKzdfMq.js";import{C as n}from"./message-components-VDKdm9Kg.js";const s={EXPLAIN_NODE:"copilot:explain-node",TOOLBOX_USAGE:"copilot:toolbox-usage",TOOLBOX_PARAMETERS:"copilot:toolbox-parameters",TOOLBOX_DOWNSTREAMNODES:"copilot:toolbox-downstreamnodes"},d=i.lazy(()=>a(()=>import(getImportPath("workflowChat-C7at41Bc.js")).then(e=>e.w),__vite__mapDeps([0,1,2,3,4,5,6])).then(e=>({default:e.default})));function c(){const[e,r]=t.useState(!1);return t.useEffect(()=>{const l=()=>{r(!0)};return window.addEventListener(s.EXPLAIN_NODE,l),()=>window.removeEventListener(s.EXPLAIN_NODE,l)},[]),o.jsx(n,{children:o.jsx("div",{className:"h-full w-full flex flex-col",children:o.jsx(t.Suspense,{fallback:o.jsx("div",{className:"h-full w-full flex items-center justify-center",children:"Loading..."}),children:o.jsx(d,{visible:!0,triggerUsage:e,onUsageTriggered:()=>r(!1)})})})})}const O=Object.freeze(Object.defineProperty({__proto__:null,default:c},Symbol.toStringTag,{value:"Module"}));export{O as A,s as C};
|
||||
import{_ as a}from"./input.js";import{j as o}from"./vendor-markdown-B4Av9P7l.js";import{r as t,R as i}from"./vendor-react-ByKzdfMq.js";import{C as n}from"./message-components-C_zeUc_b.js";const s={EXPLAIN_NODE:"copilot:explain-node",TOOLBOX_USAGE:"copilot:toolbox-usage",TOOLBOX_PARAMETERS:"copilot:toolbox-parameters",TOOLBOX_DOWNSTREAMNODES:"copilot:toolbox-downstreamnodes"},d=i.lazy(()=>a(()=>import(getImportPath("workflowChat-B4zoMsdC.js")).then(e=>e.w),__vite__mapDeps([0,1,2,3,4,5,6])).then(e=>({default:e.default})));function c(){const[e,r]=t.useState(!1);return t.useEffect(()=>{const l=()=>{r(!0)};return window.addEventListener(s.EXPLAIN_NODE,l),()=>window.removeEventListener(s.EXPLAIN_NODE,l)},[]),o.jsx(n,{children:o.jsx("div",{className:"h-full w-full flex flex-col",children:o.jsx(t.Suspense,{fallback:o.jsx("div",{className:"h-full w-full flex items-center justify-center",children:"Loading..."}),children:o.jsx(d,{visible:!0,triggerUsage:e,onUsageTriggered:()=>r(!1)})})})})}const O=Object.freeze(Object.defineProperty({__proto__:null,default:c},Symbol.toStringTag,{value:"Module"}));export{O as A,s as C};
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
function getImportPath(filename) {
|
||||
return `./${filename}`;
|
||||
}
|
||||
import{j as s}from"./vendor-markdown-B4Av9P7l.js";import{r as i}from"./vendor-react-ByKzdfMq.js";import{u as A,p as E,R as D,o as m,W as f,a as b}from"./message-components-VDKdm9Kg.js";function P({content:h,name:k="Assistant",avatar:O,onAddMessage:y,onUpdateMessage:c,messageId:_}){const{dispatch:u,abortControllerRef:l}=A(),[d,w]=i.useState(!1),[T,G]=i.useState(""),[J,S]=i.useState(!1),[j,v]=i.useState(null),r=i.useMemo(()=>{try{return JSON.parse(h)}catch(e){return console.error("Failed to parse message content:",e),null}},[h]),x=i.useMemo(()=>{if(!r||!r.ext)return null;const e=r.ext.find(t=>t.type==="debug_start_checkpoint"||t.type==="debug_checkpoint"&&t.data?.checkpoint_type==="debug_start");return e&&e.data&&e.data.checkpoint_id?e.data.checkpoint_id:null},[r])||j,N=async()=>{if(d)return;const e=m(),t={id:e,role:"ai",content:JSON.stringify({text:`🔍 Starting workflow analysis...
|
||||
import{j as s}from"./vendor-markdown-B4Av9P7l.js";import{r as i}from"./vendor-react-ByKzdfMq.js";import{u as A,p as E,R as D,o as m,W as f,a as b}from"./message-components-C_zeUc_b.js";function P({content:h,name:k="Assistant",avatar:O,onAddMessage:y,onUpdateMessage:c,messageId:_}){const{dispatch:u,abortControllerRef:l}=A(),[d,w]=i.useState(!1),[T,G]=i.useState(""),[J,S]=i.useState(!1),[j,v]=i.useState(null),r=i.useMemo(()=>{try{return JSON.parse(h)}catch(e){return console.error("Failed to parse message content:",e),null}},[h]),x=i.useMemo(()=>{if(!r||!r.ext)return null;const e=r.ext.find(t=>t.type==="debug_start_checkpoint"||t.type==="debug_checkpoint"&&t.data?.checkpoint_type==="debug_start");return e&&e.data&&e.data.checkpoint_id?e.data.checkpoint_id:null},[r])||j,N=async()=>{if(d)return;const e=m(),t={id:e,role:"ai",content:JSON.stringify({text:`🔍 Starting workflow analysis...
|
||||
`,ext:[]}),format:"markdown",name:"Assistant",finished:!1,debugGuide:!0};y?.(t);try{f.trackEvent({event_type:"debug_trigger",message_type:"debug",message_id:e,data:{}}),await C(),u({type:"SET_LOADING",payload:!0}),await I(e)}finally{w(!1)}},C=async()=>{try{const e=await b.graphToPrompt(),t=localStorage.getItem("sessionId")||"";if(t&&e){const n=await f.saveWorkflowCheckpoint(t,e.output,e.workflow,"debug_start");if(v(n.version_id),console.log(`Saved debug start checkpoint: ${n.version_id}`),c&&r){const o=[...r.ext||[]].filter(g=>g.type!=="debug_start_checkpoint"&&!(g.type==="debug_checkpoint"&&g.data?.checkpoint_type==="debug_start"));o.push({type:"debug_start_checkpoint",data:{checkpoint_id:n.version_id,checkpoint_type:"debug_start"}});const p={id:_||m(),role:"ai",content:JSON.stringify({text:r.text,ext:o}),format:"debug_guide",name:"Assistant"};c(p)}}}catch(e){console.error("Failed to save checkpoint before debug:",e)}},I=async e=>{try{const t=await b.graphToPrompt();let n="",a=null;l&&(l.current=new AbortController);for await(const o of f.streamDebugAgent(t,l?.current?.signal||void 0))if(o.text){n=o.text,o.ext&&(a=o.ext);const p={id:e,role:"ai",content:JSON.stringify({text:n,ext:a||[]}),format:"markdown",name:"Assistant",finished:!1,debugGuide:!0};c?.(p)}c?.({id:e,role:"ai",content:JSON.stringify({text:n,ext:a||[]}),format:"markdown",name:"Assistant",finished:!0,debugGuide:!0}),u({type:"SET_LOADING",payload:!1}),l.current=null}catch(t){console.error("Error calling debug agent:",t),S(!1);const a={id:e,role:"ai",content:JSON.stringify({text:`I encountered an error while analyzing your workflow: ${t.message||"Unknown error"}`,ext:[]}),format:"markdown",name:"Assistant",finished:!0};c?.(a),u({type:"SET_LOADING",payload:!1}),l.current=null}};return r?s.jsx(E,{name:k,children:s.jsxs("div",{className:"bg-gray-100 pt-4 px-4 pb-3 rounded-lg",children:[s.jsx("div",{className:"flex justify-between items-start",children:s.jsx("p",{className:"text-gray-700 text-sm flex-1",children:r.text})}),s.jsx("div",{className:"flex justify-end mt-4",children:s.jsx("button",{onClick:N,disabled:d,className:`debug-btn bg-debug-btn text-sm text-[#fff] rounded-lg px-4 py-2 ${d?"cursor-not-allowed":"cursor-pointer"}`,children:d?"Analyzing...":"Debug Errors"})}),s.jsx("div",{className:"flex justify-end mt-2",children:!!x&&s.jsx("div",{className:"ml-2 flex-shrink-0",children:s.jsx(D,{checkpointId:x,onRestore:()=>{console.log("Workflow restored from checkpoint")}})})})]})}):null}export{P as DebugGuide};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
function getImportPath(filename) {
|
||||
return `./${filename}`;
|
||||
}
|
||||
import{j as e}from"./vendor-markdown-B4Av9P7l.js";import{b as u,c as w,p as g,G as k,R as j}from"./message-components-VDKdm9Kg.js";import{r as p}from"./vendor-react-ByKzdfMq.js";import{B as v,M as N}from"./workflowChat-C7at41Bc.js";import"./input.js";/* empty css */import"./App-SJGu_4x8.js";const y=n=>{const{title:a="",isWorkflowUpdate:f=!1,className:d="",children:c}=n,[i,r]=p.useState(!1);return e.jsx(v,{className:"rounded-lg ",borderClassName:"rounded-lg",children:e.jsxs("div",{className:`flex flex-col ${d} ${i?"":"max-h-[200px]"}`,children:[e.jsxs("div",{className:"flex justify-between items-center pb-2 border-b border-gray-100",children:[e.jsx("div",{children:typeof a=="string"?e.jsx("h3",{className:"text-sm text-[#fff] font-medium",children:a}):a}),e.jsx("button",{onClick:()=>{r(!i)},children:i?e.jsx(u,{className:"text-gray-700"}):e.jsx(w,{className:"text-gray-700"})})]}),e.jsx("div",{className:"overflow-hidden flex-1",children:c})]})})};function E({content:n,name:a="Assistant",avatar:f,format:d="markdown"}){const c=p.useRef(null),i=()=>{let r=null,l=!1,o,x=[];try{if(o=JSON.parse(n),o.ext){let t=o.ext.find(s=>s.type==="workflow_rewrite_checkpoint"||s.type==="debug_checkpoint"&&s.data?.checkpoint_type==="workflow_rewrite_start");t&&t.data&&t.data.checkpoint_id?(r=t.data.checkpoint_id,l=!0):(t=o.ext.find(s=>s.type==="workflow_rewrite_complete"),t&&t.data&&t.data.version_id?(r=t.data.version_id,l=!0):(t=o.ext.find(s=>s.type==="debug_checkpoint"),t&&t.data&&t.data.checkpoint_id&&(r=t.data.checkpoint_id,l=!1))),o.ext.find(s=>s.type==="workflow_update")&&(l=!0),x=o.ext.find(s=>s.type==="param_update")?.data?.model_suggest||[]}}catch(t){return console.error("Failed to parse DebugResult content:",t),null}const h=l?e.jsxs("div",{className:"flex items-center text-green-500",children:[e.jsx("svg",{className:"w-5 h-5 mr-2",fill:"currentColor",viewBox:"0 0 20 20",children:e.jsx("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",clipRule:"evenodd"})}),e.jsx("h4",{className:"font-bold text-xl",children:"Workflow Updated Successfully"})]}):e.jsxs("div",{className:"flex items-center text-gray-900",children:[e.jsx("svg",{className:"w-5 h-5 mr-2",viewBox:"0 0 1024 1024",version:"1.1",xmlns:"http://www.w3.org/2000/svg","p-id":"28108",fill:"currentColor",children:e.jsx("path",{d:"M401.048025 844.855924c0 20.341281 16.643052 36.984333 36.984333 36.984333l147.936307 0c20.341281 0 36.984333-16.643052 36.984333-36.984333L622.952998 807.872614 401.048025 807.872614 401.048025 844.855924zM512 142.159744c-142.943596 0-258.888282 115.944686-258.888282 258.888282 0 88.021729 44.011376 165.503405 110.951975 212.288964l0 83.58365c0 20.341281 16.643052 36.984333 36.984333 36.984333l221.903949 0c20.341281 0 36.984333-16.643052 36.984333-36.984333l0-83.58365c66.941622-46.784536 110.951975-124.266212 110.951975-212.288964C770.888282 258.104429 654.943596 142.159744 512 142.159744zM617.588827 552.682561l-31.621185 22.005176 0 85.248569L438.031335 659.936307l0-85.063351L406.41015 552.866756c-49.743938-34.764781-79.33079-91.350544-79.33079-151.634536 0-101.890598 83.029018-184.92064 184.92064-184.92064s184.92064 83.029018 184.92064 184.92064C696.919617 461.332017 667.332764 517.91778 617.588827 552.682561z","p-id":"28277"})}),e.jsx("h4",{className:"font-bold text-xl",children:"Workflow Updated Finished"})]}),m=l?e.jsx("div",{className:"mt-3 text-xs text-gray-700",children:"💡 If you're not satisfied with the changes, click the restore button to revert to the previous version."}):null;return e.jsxs("div",{ref:c,className:"sticky top-0 left-0 w-full bg-gray-100 p-4 rounded-lg overflow-hidden",children:[e.jsx(y,{title:h,isWorkflowUpdate:l,className:"p-4",children:e.jsxs("div",{className:"prose prose-sm max-w-none",children:[d==="markdown"?e.jsx(k,{response:o||{}}):e.jsx("pre",{className:"whitespace-pre-wrap text-gray-700 text-sm leading-relaxed h-full",children:o?.text||""}),m]})}),x?.length>0&&e.jsx(N,{modelList:x,showPagination:!1}),e.jsx("div",{className:"flex justify-end mt-2",children:!!r&&e.jsx("div",{className:"ml-2 flex-shrink-0",children:e.jsx(j,{checkpointId:r,onRestore:()=>{console.log(`Workflow restored from ${l?"workflow update":"debug"} checkpoint`)},title:l?`Restore to this version (Version ${r})`:`Restore checkpoint ${r}`})})})]})};return e.jsx(g,{name:a,children:i()})}export{E as DebugResult};
|
||||
import{j as e}from"./vendor-markdown-B4Av9P7l.js";import{b as u,c as w,p as g,G as k,R as j}from"./message-components-C_zeUc_b.js";import{r as p}from"./vendor-react-ByKzdfMq.js";import{B as v,M as N}from"./workflowChat-B4zoMsdC.js";import"./input.js";/* empty css */import"./App-BpKMzle9.js";const y=n=>{const{title:a="",isWorkflowUpdate:f=!1,className:d="",children:c}=n,[i,r]=p.useState(!1);return e.jsx(v,{className:"rounded-lg ",borderClassName:"rounded-lg",children:e.jsxs("div",{className:`flex flex-col ${d} ${i?"":"max-h-[200px]"}`,children:[e.jsxs("div",{className:"flex justify-between items-center pb-2 border-b border-gray-100",children:[e.jsx("div",{children:typeof a=="string"?e.jsx("h3",{className:"text-sm text-[#fff] font-medium",children:a}):a}),e.jsx("button",{onClick:()=>{r(!i)},children:i?e.jsx(u,{className:"text-gray-700"}):e.jsx(w,{className:"text-gray-700"})})]}),e.jsx("div",{className:"overflow-hidden flex-1",children:c})]})})};function E({content:n,name:a="Assistant",avatar:f,format:d="markdown"}){const c=p.useRef(null),i=()=>{let r=null,l=!1,o,x=[];try{if(o=JSON.parse(n),o.ext){let t=o.ext.find(s=>s.type==="workflow_rewrite_checkpoint"||s.type==="debug_checkpoint"&&s.data?.checkpoint_type==="workflow_rewrite_start");t&&t.data&&t.data.checkpoint_id?(r=t.data.checkpoint_id,l=!0):(t=o.ext.find(s=>s.type==="workflow_rewrite_complete"),t&&t.data&&t.data.version_id?(r=t.data.version_id,l=!0):(t=o.ext.find(s=>s.type==="debug_checkpoint"),t&&t.data&&t.data.checkpoint_id&&(r=t.data.checkpoint_id,l=!1))),o.ext.find(s=>s.type==="workflow_update")&&(l=!0),x=o.ext.find(s=>s.type==="param_update")?.data?.model_suggest||[]}}catch(t){return console.error("Failed to parse DebugResult content:",t),null}const h=l?e.jsxs("div",{className:"flex items-center text-green-500",children:[e.jsx("svg",{className:"w-5 h-5 mr-2",fill:"currentColor",viewBox:"0 0 20 20",children:e.jsx("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",clipRule:"evenodd"})}),e.jsx("h4",{className:"font-bold text-xl",children:"Workflow Updated Successfully"})]}):e.jsxs("div",{className:"flex items-center text-gray-900",children:[e.jsx("svg",{className:"w-5 h-5 mr-2",viewBox:"0 0 1024 1024",version:"1.1",xmlns:"http://www.w3.org/2000/svg","p-id":"28108",fill:"currentColor",children:e.jsx("path",{d:"M401.048025 844.855924c0 20.341281 16.643052 36.984333 36.984333 36.984333l147.936307 0c20.341281 0 36.984333-16.643052 36.984333-36.984333L622.952998 807.872614 401.048025 807.872614 401.048025 844.855924zM512 142.159744c-142.943596 0-258.888282 115.944686-258.888282 258.888282 0 88.021729 44.011376 165.503405 110.951975 212.288964l0 83.58365c0 20.341281 16.643052 36.984333 36.984333 36.984333l221.903949 0c20.341281 0 36.984333-16.643052 36.984333-36.984333l0-83.58365c66.941622-46.784536 110.951975-124.266212 110.951975-212.288964C770.888282 258.104429 654.943596 142.159744 512 142.159744zM617.588827 552.682561l-31.621185 22.005176 0 85.248569L438.031335 659.936307l0-85.063351L406.41015 552.866756c-49.743938-34.764781-79.33079-91.350544-79.33079-151.634536 0-101.890598 83.029018-184.92064 184.92064-184.92064s184.92064 83.029018 184.92064 184.92064C696.919617 461.332017 667.332764 517.91778 617.588827 552.682561z","p-id":"28277"})}),e.jsx("h4",{className:"font-bold text-xl",children:"Workflow Updated Finished"})]}),m=l?e.jsx("div",{className:"mt-3 text-xs text-gray-700",children:"💡 If you're not satisfied with the changes, click the restore button to revert to the previous version."}):null;return e.jsxs("div",{ref:c,className:"sticky top-0 left-0 w-full bg-gray-100 p-4 rounded-lg overflow-hidden",children:[e.jsx(y,{title:h,isWorkflowUpdate:l,className:"p-4",children:e.jsxs("div",{className:"prose prose-sm max-w-none",children:[d==="markdown"?e.jsx(k,{response:o||{}}):e.jsx("pre",{className:"whitespace-pre-wrap text-gray-700 text-sm leading-relaxed h-full",children:o?.text||""}),m]})}),x?.length>0&&e.jsx(N,{modelList:x,showPagination:!1}),e.jsx("div",{className:"flex justify-end mt-2",children:!!r&&e.jsx("div",{className:"ml-2 flex-shrink-0",children:e.jsx(j,{checkpointId:r,onRestore:()=>{console.log(`Workflow restored from ${l?"workflow update":"debug"} checkpoint`)},title:l?`Restore to this version (Version ${r})`:`Restore checkpoint ${r}`})})})]})};return e.jsx(g,{name:a,children:i()})}export{E as DebugResult};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+88
-88
File diff suppressed because one or more lines are too long
+147
File diff suppressed because one or more lines are too long
-134
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "ComfyUI-Copilot"
|
||||
description = "Your Intelligent Assistant for Comfy-UI."
|
||||
version = "2.0.15"
|
||||
version = "2.0.16"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ai-business-hql qingli.hql@alibaba-inc.com
|
||||
* @Date: 2025-06-24 16:29:05
|
||||
* @LastEditors: ai-business-hql ai.bussiness.hql@gmail.com
|
||||
* @LastEditTime: 2025-09-02 17:08:42
|
||||
* @LastEditTime: 2025-09-29 17:43:30
|
||||
* @FilePath: /comfyui_copilot/ui/src/apis/workflowChatApi.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
@@ -34,13 +34,17 @@ const setApiKey = (apiKey: string) => {
|
||||
// Get OpenAI configuration from localStorage
|
||||
const getOpenAiConfig = () => {
|
||||
const openaiApiKey = localStorage.getItem('openaiApiKey');
|
||||
const openaiBaseUrl = localStorage.getItem('openaiBaseUrl') || 'https://api.openai.com/v1';
|
||||
const openaiBaseUrl = localStorage.getItem('openaiBaseUrl');
|
||||
const rsaPublicKey = localStorage.getItem('rsaPublicKey');
|
||||
const workflowLLMApiKey = localStorage.getItem('workflowLLMApiKey');
|
||||
const workflowLLMBaseUrl = localStorage.getItem('workflowLLMBaseUrl');
|
||||
|
||||
return {
|
||||
openaiApiKey: openaiApiKey || '',
|
||||
openaiBaseUrl,
|
||||
rsaPublicKey
|
||||
openaiBaseUrl: openaiBaseUrl || '',
|
||||
rsaPublicKey,
|
||||
workflowLLMApiKey: workflowLLMApiKey || '',
|
||||
workflowLLMBaseUrl: workflowLLMBaseUrl || '',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -141,7 +145,7 @@ export namespace WorkflowChatAPI {
|
||||
try {
|
||||
const apiKey = getApiKey();
|
||||
const browserLanguage = app.extensionManager.setting.get('Comfy.Locale');
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey } = getOpenAiConfig();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey, workflowLLMApiKey, workflowLLMBaseUrl } = getOpenAiConfig();
|
||||
// Generate a unique message ID for this chat request
|
||||
const messageId = generateUUID();
|
||||
|
||||
@@ -258,6 +262,13 @@ export namespace WorkflowChatAPI {
|
||||
console.error('Error encrypting OpenAI API key:', error);
|
||||
}
|
||||
}
|
||||
// Add Workflow LLM headers if available
|
||||
if (workflowLLMBaseUrl) {
|
||||
headers['Workflow-LLM-Base-Url'] = workflowLLMBaseUrl;
|
||||
}
|
||||
if (workflowLLMApiKey) {
|
||||
headers['Workflow-LLM-Api-Key'] = workflowLLMApiKey;
|
||||
}
|
||||
|
||||
// Create controller and combine with external signal if provided
|
||||
const controller = new AbortController();
|
||||
@@ -350,7 +361,7 @@ export namespace WorkflowChatAPI {
|
||||
try {
|
||||
const apiKey = getApiKey();
|
||||
const browserLanguage = getBrowserLanguage();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey } = getOpenAiConfig();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey, workflowLLMApiKey, workflowLLMBaseUrl } = getOpenAiConfig();
|
||||
|
||||
// Prepare headers
|
||||
const headers: Record<string, string> = {
|
||||
@@ -372,6 +383,13 @@ export namespace WorkflowChatAPI {
|
||||
console.error('Error encrypting OpenAI API key:', error);
|
||||
}
|
||||
}
|
||||
// Add Workflow LLM headers if available
|
||||
if (workflowLLMBaseUrl) {
|
||||
headers['Workflow-LLM-Base-Url'] = workflowLLMBaseUrl;
|
||||
}
|
||||
if (workflowLLMApiKey) {
|
||||
headers['Workflow-LLM-Api-Key'] = workflowLLMApiKey;
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/chat/get_optimized_workflow`, {
|
||||
method: 'POST',
|
||||
@@ -402,7 +420,7 @@ export namespace WorkflowChatAPI {
|
||||
export async function batchGetNodeInfo(nodeTypes: string[]): Promise<any> {
|
||||
const apiKey = getApiKey();
|
||||
const browserLanguage = getBrowserLanguage();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey } = getOpenAiConfig();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey, workflowLLMApiKey, workflowLLMBaseUrl } = getOpenAiConfig();
|
||||
|
||||
// Prepare headers
|
||||
const headers: Record<string, string> = {
|
||||
@@ -422,6 +440,13 @@ export namespace WorkflowChatAPI {
|
||||
console.error('Error encrypting OpenAI API key:', error);
|
||||
}
|
||||
}
|
||||
// Add Workflow LLM headers if available
|
||||
if (workflowLLMBaseUrl) {
|
||||
headers['Workflow-LLM-Base-Url'] = workflowLLMBaseUrl;
|
||||
}
|
||||
if (workflowLLMApiKey) {
|
||||
headers['Workflow-LLM-Api-Key'] = workflowLLMApiKey;
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/chat/get_node_info_by_types`, {
|
||||
method: 'POST',
|
||||
@@ -451,65 +476,6 @@ export namespace WorkflowChatAPI {
|
||||
return JSON.parse(cachedMessages) as Message[];
|
||||
}
|
||||
return [];
|
||||
const apiKey = getApiKey();
|
||||
const browserLanguage = getBrowserLanguage();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey } = getOpenAiConfig();
|
||||
|
||||
// Prepare headers
|
||||
const headers: Record<string, string> = {
|
||||
'accept': 'application/json',
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'trace-id': generateUUID(),
|
||||
'Accept-Language': browserLanguage,
|
||||
};
|
||||
|
||||
// Add OpenAI configuration headers if available
|
||||
if (openaiApiKey && openaiApiKey.trim() !== '' && rsaPublicKey) {
|
||||
try {
|
||||
const encryptedApiKey = await encryptWithRsaPublicKey(openaiApiKey as string, rsaPublicKey as string);
|
||||
headers['Encrypted-Openai-Api-Key'] = encryptedApiKey;
|
||||
headers['Openai-Base-Url'] = openaiBaseUrl;
|
||||
} catch (error) {
|
||||
console.error('Error encrypting OpenAI API key:', error);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/chat/get_messages_by_session_id?session_id=${sessionId}`, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
});
|
||||
|
||||
checkAndSaveApiKey(response);
|
||||
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
const message = result.message || 'Failed to fetch messages';
|
||||
alert(message + ', please refresh the page and try again.');
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const messages = result.data.map((msg: any) => {
|
||||
console.log("msg.ext", msg.ext);
|
||||
return {
|
||||
id: msg.id.toString(),
|
||||
content: msg.role === 'ai' ? JSON.stringify({
|
||||
session_id: sessionId,
|
||||
text: msg.content,
|
||||
finished: msg.finished || false,
|
||||
format: msg.format || 'markdown',
|
||||
ext: msg.ext
|
||||
}) : msg.content,
|
||||
role: msg.role,
|
||||
name: msg.role === 'ai' ? 'Assistant' : undefined,
|
||||
format: msg.format || 'markdown',
|
||||
finished: msg.finished
|
||||
}
|
||||
});
|
||||
|
||||
// Cache the messages in localStorage
|
||||
localStorage.setItem(`messages_${sessionId}`, JSON.stringify(messages));
|
||||
|
||||
return messages;
|
||||
} catch (error) {
|
||||
console.error('Error fetching messages:', error);
|
||||
alert(error instanceof Error ? error.message : 'Failed to fetch messages' + ', please refresh the page and try again.');
|
||||
@@ -521,7 +487,6 @@ export namespace WorkflowChatAPI {
|
||||
try {
|
||||
const apiKey = getApiKey();
|
||||
const browserLanguage = getBrowserLanguage();
|
||||
const { openaiApiKey, openaiBaseUrl, rsaPublicKey } = getOpenAiConfig();
|
||||
|
||||
// Prepare headers
|
||||
const headers: Record<string, string> = {
|
||||
@@ -531,17 +496,6 @@ export namespace WorkflowChatAPI {
|
||||
'Accept-Language': browserLanguage,
|
||||
};
|
||||
|
||||
// Add OpenAI configuration headers if available
|
||||
if (openaiApiKey && openaiApiKey.trim() !== '' && rsaPublicKey) {
|
||||
try {
|
||||
const encryptedApiKey = await encryptWithRsaPublicKey(openaiApiKey as string, rsaPublicKey as string);
|
||||
headers['Encrypted-Openai-Api-Key'] = encryptedApiKey;
|
||||
headers['Openai-Base-Url'] = openaiBaseUrl;
|
||||
} catch (error) {
|
||||
console.error('Error encrypting OpenAI API key:', error);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/chat/announcement`, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
@@ -651,7 +605,7 @@ export namespace WorkflowChatAPI {
|
||||
abortSignal?: AbortSignal
|
||||
): AsyncGenerator<ChatResponse> {
|
||||
try {
|
||||
const { openaiApiKey, openaiBaseUrl } = getOpenAiConfig();
|
||||
const { openaiApiKey, openaiBaseUrl, workflowLLMApiKey, workflowLLMBaseUrl } = getOpenAiConfig();
|
||||
const browserLanguage = app.extensionManager.setting.get('Comfy.Locale');
|
||||
const session_id = localStorage.getItem("sessionId") || null;
|
||||
const apiKey = getApiKey();
|
||||
@@ -670,6 +624,13 @@ export namespace WorkflowChatAPI {
|
||||
headers['Openai-Api-Key'] = openaiApiKey;
|
||||
headers['Openai-Base-Url'] = openaiBaseUrl;
|
||||
}
|
||||
// Add Workflow LLM headers if available
|
||||
if (workflowLLMBaseUrl) {
|
||||
headers['Workflow-LLM-Base-Url'] = workflowLLMBaseUrl;
|
||||
}
|
||||
if (workflowLLMApiKey) {
|
||||
headers['Workflow-LLM-Api-Key'] = workflowLLMApiKey;
|
||||
}
|
||||
|
||||
// Create controller and combine with external signal if provided
|
||||
const controller = new AbortController();
|
||||
|
||||
@@ -45,12 +45,17 @@ export function ApiKeyModal({ isOpen, onClose, onSave, initialApiKey = '', onCon
|
||||
|
||||
// OpenAI configuration
|
||||
const [openaiApiKey, setOpenaiApiKey] = useState('');
|
||||
const [openaiBaseUrl, setOpenaiBaseUrl] = useState('https://api.openai.com/v1');
|
||||
const [openaiBaseUrl, setOpenaiBaseUrl] = useState('');
|
||||
const [showOpenaiApiKey, setShowOpenaiApiKey] = useState(false);
|
||||
const [verifyingKey, setVerifyingKey] = useState(false);
|
||||
const [verificationResult, setVerificationResult] = useState<{success: boolean, message: string} | null>(null);
|
||||
const [rsaPublicKey, setRsaPublicKey] = useState<string | null>(null);
|
||||
|
||||
// Workflow LLM configuration
|
||||
const [workflowLLMApiKey, setWorkflowLLMApiKey] = useState('');
|
||||
const [workflowLLMBaseUrl, setWorkflowLLMBaseUrl] = useState('');
|
||||
const [showWorkflowLLMApiKey, setShowWorkflowLLMApiKey] = useState(false);
|
||||
|
||||
const { apikeymodel_title } = useLanguage();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -59,6 +64,8 @@ export function ApiKeyModal({ isOpen, onClose, onSave, initialApiKey = '', onCon
|
||||
// Load OpenAI configuration from localStorage
|
||||
const savedOpenaiApiKey = localStorage.getItem('openaiApiKey');
|
||||
const savedOpenaiBaseUrl = localStorage.getItem('openaiBaseUrl');
|
||||
const savedWorkflowLLMApiKey = localStorage.getItem('workflowLLMApiKey');
|
||||
const savedWorkflowLLMBaseUrl = localStorage.getItem('workflowLLMBaseUrl');
|
||||
|
||||
if (savedOpenaiApiKey) {
|
||||
setOpenaiApiKey(savedOpenaiApiKey);
|
||||
@@ -67,6 +74,12 @@ export function ApiKeyModal({ isOpen, onClose, onSave, initialApiKey = '', onCon
|
||||
if (savedOpenaiBaseUrl) {
|
||||
setOpenaiBaseUrl(savedOpenaiBaseUrl);
|
||||
}
|
||||
if (savedWorkflowLLMApiKey) {
|
||||
setWorkflowLLMApiKey(savedWorkflowLLMApiKey);
|
||||
}
|
||||
if (savedWorkflowLLMBaseUrl) {
|
||||
setWorkflowLLMBaseUrl(savedWorkflowLLMBaseUrl);
|
||||
}
|
||||
|
||||
// Fetch RSA public key
|
||||
const fetchPublicKey = async () => {
|
||||
@@ -173,22 +186,44 @@ export function ApiKeyModal({ isOpen, onClose, onSave, initialApiKey = '', onCon
|
||||
|
||||
// Check if OpenAI configuration has changed
|
||||
const previousOpenaiApiKey = localStorage.getItem('openaiApiKey') || '';
|
||||
const previousOpenaiBaseUrl = localStorage.getItem('openaiBaseUrl') || 'https://api.openai.com/v1';
|
||||
const hasOpenaiConfigChanged = openaiApiKey.trim() !== previousOpenaiApiKey || openaiBaseUrl !== previousOpenaiBaseUrl;
|
||||
const previousOpenaiBaseUrl = localStorage.getItem('openaiBaseUrl') || '';
|
||||
const hasOpenaiConfigChanged = openaiApiKey.trim() !== previousOpenaiApiKey || openaiBaseUrl.trim() !== previousOpenaiBaseUrl;
|
||||
|
||||
// Always persist base URL (needed for LMStudio which may not require API key)
|
||||
localStorage.setItem('openaiBaseUrl', openaiBaseUrl);
|
||||
// Check if Workflow LLM configuration has changed
|
||||
const previousWorkflowLLMApiKey = localStorage.getItem('workflowLLMApiKey') || '';
|
||||
const previousWorkflowLLMBaseUrl = localStorage.getItem('workflowLLMBaseUrl') || '';
|
||||
const hasWorkflowConfigChanged = workflowLLMApiKey.trim() !== previousWorkflowLLMApiKey || workflowLLMBaseUrl.trim() !== previousWorkflowLLMBaseUrl;
|
||||
|
||||
// Save or clear OpenAI base URL
|
||||
if (openaiBaseUrl.trim()) {
|
||||
localStorage.setItem('openaiBaseUrl', openaiBaseUrl.trim());
|
||||
} else {
|
||||
localStorage.removeItem('openaiBaseUrl');
|
||||
}
|
||||
|
||||
// Save or clear Workflow LLM base URL
|
||||
if (workflowLLMBaseUrl.trim()) {
|
||||
localStorage.setItem('workflowLLMBaseUrl', workflowLLMBaseUrl.trim());
|
||||
} else {
|
||||
localStorage.removeItem('workflowLLMBaseUrl');
|
||||
}
|
||||
|
||||
// Save or clear OpenAI API key in localStorage
|
||||
if (openaiApiKey.trim()) {
|
||||
localStorage.setItem('openaiApiKey', openaiApiKey);
|
||||
localStorage.setItem('openaiApiKey', openaiApiKey.trim());
|
||||
} else {
|
||||
// If the OpenAI API key is empty, remove only the key and keep base URL
|
||||
localStorage.removeItem('openaiApiKey');
|
||||
}
|
||||
|
||||
// Save or clear Workflow LLM API key
|
||||
if (workflowLLMApiKey.trim()) {
|
||||
localStorage.setItem('workflowLLMApiKey', workflowLLMApiKey);
|
||||
} else {
|
||||
localStorage.removeItem('workflowLLMApiKey');
|
||||
}
|
||||
|
||||
// Call configuration updated callback if OpenAI config has changed
|
||||
if (hasOpenaiConfigChanged && onConfigurationUpdated) {
|
||||
if ((hasOpenaiConfigChanged || hasWorkflowConfigChanged) && onConfigurationUpdated) {
|
||||
onConfigurationUpdated();
|
||||
}
|
||||
|
||||
@@ -377,6 +412,74 @@ export function ApiKeyModal({ isOpen, onClose, onSave, initialApiKey = '', onCon
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleCard>
|
||||
{/* Workflow LLM Configuration (Optional) */}
|
||||
<CollapsibleCard
|
||||
title={<h3 className="text-sm text-gray-900 dark:text-white font-medium">Workflow LLM Configuration (Optional)</h3>}
|
||||
className='mb-4'
|
||||
>
|
||||
<div>
|
||||
{/* Workflow LLM API Key */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Workflow LLM API Key
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showWorkflowLLMApiKey ? "text" : "password"}
|
||||
value={workflowLLMApiKey}
|
||||
onChange={(e) => setWorkflowLLMApiKey(e.target.value)}
|
||||
placeholder="Enter workflow LLM API key (if required)"
|
||||
className="w-full px-4 py-3 border border-gray-200 dark:border-gray-600 rounded-lg pr-12 text-xs
|
||||
bg-gray-50 dark:bg-gray-700
|
||||
text-gray-900 dark:text-white
|
||||
placeholder-gray-500 dark:placeholder-gray-400
|
||||
focus:border-blue-500 dark:focus:border-blue-400
|
||||
focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20
|
||||
focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute inset-y-0 right-0 pr-4 flex items-center text-gray-500 dark:text-gray-400
|
||||
hover:text-gray-700 dark:hover:text-gray-200 transition-colors bg-transparent border-none"
|
||||
onClick={() => setShowWorkflowLLMApiKey(!showWorkflowLLMApiKey)}
|
||||
>
|
||||
{showWorkflowLLMApiKey ? (
|
||||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Workflow LLM Base URL */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Workflow LLM Server URL
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={workflowLLMBaseUrl}
|
||||
onChange={(e) => setWorkflowLLMBaseUrl(e.target.value)}
|
||||
placeholder="e.g. https://api.openai.com/v1 or http://localhost:1234/v1"
|
||||
className="w-full px-4 py-3 border border-gray-200 dark:border-gray-600 rounded-lg text-xs
|
||||
bg-gray-50 dark:bg-gray-700
|
||||
text-gray-900 dark:text-white
|
||||
placeholder-gray-500 dark:placeholder-gray-400
|
||||
focus:border-blue-500 dark:focus:border-blue-400
|
||||
focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20
|
||||
focus:outline-none"
|
||||
/>
|
||||
<div className="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
<div className="mb-1"><strong>Optional:</strong> Only set if your workflow tools use a different LLM.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleCard>
|
||||
{/* Action Buttons */}
|
||||
<div className="flex justify-end gap-3">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user