Add Agent capabilities and configurations across the application
- Extend the AssistantConfig model to include agent_interface_type, agent_values, and agent_secrets for enhanced agent management. - Update ModelType to include "Agent" for broader capability support. - Seed new agent models and configurations in the database for Dify, FastGPT, and OpenCode applications. - Modify the Assistant routes to validate and handle agent capabilities. - Implement agent resource resolution in the config resolver for runtime configuration. - Enhance the interface catalog with agent-specific fields and definitions. - Update frontend components to manage agent configurations, including form handling and state management for agent-related inputs.
This commit is contained in:
@@ -39,10 +39,13 @@ VALUES
|
||||
('asst_002', 'TTS', 'model_004', '{}'),
|
||||
('asst_003', 'ASR', 'model_002', '{}'),
|
||||
('asst_003', 'TTS', 'model_004', '{}'),
|
||||
('asst_003', 'Agent', 'model_014', '{}'),
|
||||
('asst_004', 'ASR', 'model_002', '{}'),
|
||||
('asst_004', 'TTS', 'model_004', '{}'),
|
||||
('asst_004', 'Agent', 'model_015', '{}'),
|
||||
('asst_005', 'ASR', 'model_002', '{}'),
|
||||
('asst_005', 'TTS', 'model_004', '{}')
|
||||
('asst_005', 'TTS', 'model_004', '{}'),
|
||||
('asst_005', 'Agent', 'model_016', '{}')
|
||||
ON CONFLICT (assistant_id, capability) DO UPDATE SET
|
||||
model_resource_id = EXCLUDED.model_resource_id,
|
||||
updated_at = now();
|
||||
|
||||
@@ -40,6 +40,22 @@ VALUES
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":false}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('dify', 'Dify Agent', 'Agent',
|
||||
$$ {"fields":[
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('fastgpt', 'FastGPT Agent', 'Agent',
|
||||
$$ {"fields":[
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"appId","label":"App ID","group":"values","type":"text","required":true}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('opencode', 'OpenCode Agent', 'Agent',
|
||||
$$ {"fields":[
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('stepfun-realtime', 'StepFun StepAudio Realtime', 'Realtime',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
|
||||
@@ -41,6 +41,15 @@ VALUES
|
||||
'{"apiKey":"replace-me"}', FALSE, TRUE, FALSE),
|
||||
('model_013', 'doubao-seed-2-0-mini-260428', 'LLM', 'openai-llm',
|
||||
'{"modelId":"doubao-seed-2-0-mini-260428","apiUrl":"https://ark.cn-beijing.volces.com/api/v3","temperature":0.7,"extraBody":{"thinking":{"type":"disabled"}}}',
|
||||
'{"apiKey":"replace-me"}', TRUE, TRUE, FALSE)
|
||||
'{"apiKey":"replace-me"}', TRUE, TRUE, FALSE),
|
||||
('model_014', 'Dify 客服应用', 'Agent', 'dify',
|
||||
'{"apiUrl":"https://api.dify.ai/v1/chat-messages"}',
|
||||
'{"apiKey":"replace-me"}', FALSE, TRUE, FALSE),
|
||||
('model_015', 'FastGPT 售后应用', 'Agent', 'fastgpt',
|
||||
'{"apiUrl":"https://api.fastgpt.in/api/v1/chat/completions","appId":"app-fastgpt-001"}',
|
||||
'{"apiKey":"replace-me"}', FALSE, TRUE, FALSE),
|
||||
('model_016', 'OpenCode 服务', 'Agent', 'opencode',
|
||||
'{"apiUrl":"http://localhost:4096"}',
|
||||
'{"apiKey":"replace-me"}', FALSE, TRUE, FALSE)
|
||||
-- Seed defaults must never overwrite resources configured through the UI.
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
@@ -36,6 +36,9 @@ class AssistantConfig(BaseModel):
|
||||
realtime_interface_type: str = ""
|
||||
realtime_values: dict = {}
|
||||
realtime_secrets: dict = {}
|
||||
agent_interface_type: str = ""
|
||||
agent_values: dict = {}
|
||||
agent_secrets: dict = {}
|
||||
llm_interface_type: str = "openai-llm"
|
||||
stt_interface_type: str = "openai-asr"
|
||||
tts_interface_type: str = "openai-tts"
|
||||
|
||||
@@ -12,7 +12,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
router = APIRouter(prefix="/api/assistants", tags=["assistants"])
|
||||
CAPABILITIES = ("LLM", "ASR", "TTS", "Realtime", "Embedding")
|
||||
CAPABILITIES = ("LLM", "ASR", "TTS", "Realtime", "Embedding", "Agent")
|
||||
|
||||
|
||||
def _validate_workflow(body: AssistantUpsert) -> None:
|
||||
|
||||
@@ -13,7 +13,7 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
RuntimeMode = Literal["pipeline", "realtime"]
|
||||
ModelType = Literal["LLM", "ASR", "TTS", "Realtime", "Embedding"]
|
||||
ModelType = Literal["LLM", "ASR", "TTS", "Realtime", "Embedding", "Agent"]
|
||||
AssistantType = Literal["prompt", "workflow", "dify", "fastgpt", "opencode"]
|
||||
|
||||
# 外部应用类型:其 config.apiKey 是该助手私有密钥,读时打码 / 写时哨兵
|
||||
|
||||
@@ -33,6 +33,36 @@ async def _resource_for(
|
||||
return resource
|
||||
|
||||
|
||||
async def _agent_resource_for(
|
||||
session: AsyncSession,
|
||||
assistant_id: str,
|
||||
interface_type: str,
|
||||
) -> ModelResource | None:
|
||||
if interface_type not in {"dify", "fastgpt", "opencode"}:
|
||||
return None
|
||||
binding = await session.get(AssistantModelBinding, (assistant_id, "Agent"))
|
||||
resource_id = binding.model_resource_id if binding else None
|
||||
resource = await session.get(ModelResource, resource_id) if resource_id else None
|
||||
if (
|
||||
resource
|
||||
and resource.capability == "Agent"
|
||||
and resource.interface_type == interface_type
|
||||
):
|
||||
return resource
|
||||
return (
|
||||
await session.execute(
|
||||
select(ModelResource)
|
||||
.where(
|
||||
ModelResource.capability == "Agent",
|
||||
ModelResource.interface_type == interface_type,
|
||||
ModelResource.enabled.is_(True),
|
||||
)
|
||||
.order_by(ModelResource.is_default.desc(), ModelResource.id.asc())
|
||||
.limit(1)
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
|
||||
|
||||
def _value(resource: ModelResource | None, key: str, default):
|
||||
if not resource:
|
||||
return default
|
||||
@@ -58,6 +88,7 @@ async def resolve_runtime_config(
|
||||
stt_resource = await _resource_for(session, assistant.id, "ASR")
|
||||
tts_resource = await _resource_for(session, assistant.id, "TTS")
|
||||
realtime_resource = await _resource_for(session, assistant.id, "Realtime")
|
||||
agent_resource = await _agent_resource_for(session, assistant.id, assistant.type)
|
||||
vision_resource = (
|
||||
await session.get(ModelResource, assistant.vision_model_resource_id)
|
||||
if assistant.vision_model_resource_id
|
||||
@@ -75,9 +106,9 @@ async def resolve_runtime_config(
|
||||
# workflow 图:仅 workflow 类型非空,引擎据此启用图驱动对话
|
||||
graph=(assistant.graph or {}) if assistant.type == "workflow" else {},
|
||||
# 外部托管类型连接信息(DB 存真 key,直接注入)
|
||||
fastgpt_api_url=assistant.api_url,
|
||||
fastgpt_api_key=assistant.api_key,
|
||||
fastgpt_app_id=assistant.app_id,
|
||||
fastgpt_api_url=str(_value(agent_resource, "apiUrl", assistant.api_url)),
|
||||
fastgpt_api_key=_secret(agent_resource, "apiKey", assistant.api_key),
|
||||
fastgpt_app_id=str(_value(agent_resource, "appId", assistant.app_id)),
|
||||
# 模型/音色:模型资源中的配置优先
|
||||
model=str(_value(llm_resource, "modelId", "")),
|
||||
asr=str(_value(stt_resource, "modelId", "")),
|
||||
@@ -114,6 +145,9 @@ async def resolve_runtime_config(
|
||||
),
|
||||
realtime_values=(realtime_resource.values or {}) if realtime_resource else {},
|
||||
realtime_secrets=(realtime_resource.secrets or {}) if realtime_resource else {},
|
||||
agent_interface_type=(agent_resource.interface_type if agent_resource else ""),
|
||||
agent_values=(agent_resource.values or {}) if agent_resource else {},
|
||||
agent_secrets=(agent_resource.secrets or {}) if agent_resource else {},
|
||||
# 运行时连接信息(真 key + url):模型资源优先,否则 .env 兜底
|
||||
llm_api_key=_secret(llm_resource, "apiKey", config.LLM_API_KEY),
|
||||
llm_base_url=str(_value(llm_resource, "apiUrl", config.LLM_BASE_URL)),
|
||||
|
||||
@@ -34,6 +34,10 @@ OPENAI_COMMON = [
|
||||
field("apiUrl", "API URL", type_="url", required=True),
|
||||
field("apiKey", "API Key", group="secrets", type_="password", required=True),
|
||||
]
|
||||
AGENT_COMMON = [
|
||||
field("apiUrl", "API URL", type_="url", required=True),
|
||||
field("apiKey", "API Key", group="secrets", type_="password", required=True),
|
||||
]
|
||||
XFYUN_AUTH = [
|
||||
field("apiUrl", "WebSocket URL", type_="url", required=True),
|
||||
field("appId", "App ID", group="secrets", type_="password", required=True),
|
||||
@@ -78,6 +82,24 @@ INTERFACE_DEFINITIONS: list[dict] = [
|
||||
"capability": "Realtime",
|
||||
"fields": OPENAI_COMMON + [field("voice", "Voice")],
|
||||
},
|
||||
{
|
||||
"interface_type": "dify",
|
||||
"name": "Dify Agent",
|
||||
"capability": "Agent",
|
||||
"fields": AGENT_COMMON,
|
||||
},
|
||||
{
|
||||
"interface_type": "fastgpt",
|
||||
"name": "FastGPT Agent",
|
||||
"capability": "Agent",
|
||||
"fields": AGENT_COMMON + [field("appId", "App ID", required=True)],
|
||||
},
|
||||
{
|
||||
"interface_type": "opencode",
|
||||
"name": "OpenCode Agent",
|
||||
"capability": "Agent",
|
||||
"fields": AGENT_COMMON,
|
||||
},
|
||||
{
|
||||
"interface_type": "stepfun-realtime",
|
||||
"name": "StepFun StepAudio Realtime",
|
||||
|
||||
Reference in New Issue
Block a user