Skip to content

Commit 5c68af4

Browse files
authored
✨ Skill ability enhancement and bug fix (#3017)
* ✨ Support skill params config * ✨ Add frontend official skill install wizard, which could be triggered by tenant creation or admin management ♻️ Remove terminal skill installation in deploy.sh ✨ Users can now manage skills in tenant resource page * ♻️ Remove terminal skill installation in deploy.sh ♻️ Change default skill install directory to $ROOT_DIR/nexent-data * ✨ Supports tenant-level separation of skills 🐛 Bugfix: code retrieved from knowledge base would falsely be executed * ♻️ Now constraint and few shot tab would remain empty when generating agent prompt ♻️ Add backend support to skill-tenant management and official skill installation * ✨ Agent export/import now includes skills * 🐛 Bugfix: frontend syntax error * 🐛 Bugfix: frontend syntax error 🧪 Add unit tests * ♻️ Refactor: remove redundant code 🧪 Add unit tests * 🐛 Bugfix: frontend syntax error * 🧪 Fix test files * 🐛 Bugfix: frontend syntax error * 🐛 Bugfix: frontend syntax error * 🧪 Fix test files * 🐛 Bugfix: SU skill resource management with wrong scope * 🐛 Bugfix: SU skill resource management with wrong scope
1 parent 8ad3cb6 commit 5c68af4

82 files changed

Lines changed: 10672 additions & 3293 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/apps/agent_app.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import json
12
import logging
23
from http import HTTPStatus
34
from typing import Optional
45

56
from fastapi import APIRouter, Body, Header, HTTPException, Request, Query
67
from fastapi.encoders import jsonable_encoder
7-
from starlette.responses import JSONResponse
8+
from starlette.responses import JSONResponse, Response
89

910
from consts.model import AgentRequest, AgentInfoRequest, AgentIDRequest, ConversationResponse, AgentImportRequest, AgentNameBatchCheckRequest, AgentNameBatchRegenerateRequest, VersionPublishRequest, VersionListResponse, VersionDetailResponse, VersionRollbackRequest, VersionStatusRequest, CurrentVersionResponse, VersionCompareRequest, VersionUpdateRequest
11+
from consts.exceptions import SkillDuplicateError
1012
from services.agent_service import (
1113
get_agent_info_impl,
1214
get_creating_sub_agent_info_impl,
@@ -22,6 +24,8 @@
2224
get_agent_call_relationship_impl,
2325
clear_agent_new_mark_impl,
2426
get_agent_by_name_impl,
27+
export_agent_with_skills_impl,
28+
import_agent_with_skills_impl,
2529
)
2630
from services.agent_version_service import (
2731
publish_version_impl,
@@ -167,11 +171,24 @@ async def delete_agent_api(
167171
@agent_config_router.post("/export")
168172
async def export_agent_api(request: AgentIDRequest, authorization: Optional[str] = Header(None)):
169173
"""
170-
export an agent
174+
export an agent.
175+
176+
Returns a ZIP file if the agent has skill instances, otherwise returns plain JSON.
177+
The response Content-Type and body differ based on the agent's skill configuration.
171178
"""
172179
try:
173-
agent_info_str = await export_agent_impl(request.agent_id, authorization)
174-
return ConversationResponse(code=0, message="success", data=agent_info_str)
180+
result = await export_agent_with_skills_impl(request.agent_id, authorization)
181+
if isinstance(result, dict) and result.get("_zip"):
182+
return Response(
183+
content=result["data"],
184+
media_type="application/zip",
185+
headers={
186+
"Content-Disposition": f"attachment; filename=\"{result.get('filename', 'agent_export.zip')}\""
187+
}
188+
)
189+
if isinstance(result, str):
190+
result = json.loads(result)
191+
return ConversationResponse(code=0, message="success", data=result)
175192
except Exception as e:
176193
logger.error(f"Agent export error: {str(e)}")
177194
raise HTTPException(
@@ -181,15 +198,32 @@ async def export_agent_api(request: AgentIDRequest, authorization: Optional[str]
181198
@agent_config_router.post("/import")
182199
async def import_agent_api(request: AgentImportRequest, authorization: Optional[str] = Header(None)):
183200
"""
184-
import an agent
201+
import an agent.
202+
203+
Accepts both plain JSON (agent without skills) and JSON with embedded skill ZIPs
204+
(agent with skills). The skills field, if present, should contain base64-encoded
205+
ZIP packages for each skill.
185206
"""
186207
try:
187-
await import_agent_impl(
188-
request.agent_info,
189-
authorization,
190-
force_import=request.force_import
191-
)
208+
if request.skills:
209+
await import_agent_with_skills_impl(
210+
request.agent_info,
211+
request.skills,
212+
authorization,
213+
force_import=request.force_import
214+
)
215+
else:
216+
await import_agent_impl(
217+
request.agent_info,
218+
authorization,
219+
force_import=request.force_import
220+
)
192221
return {}
222+
except SkillDuplicateError as exc:
223+
raise HTTPException(status_code=409, detail={
224+
"type": "skill_duplicate",
225+
"duplicate_skills": exc.duplicate_names
226+
})
193227
except Exception as e:
194228
logger.error(f"Agent import error: {str(e)}")
195229
raise HTTPException(

backend/apps/prompt_app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ async def generate_and_save_system_prompt_api(
3434
language=language,
3535
tool_ids=prompt_request.tool_ids,
3636
sub_agent_ids=prompt_request.sub_agent_ids,
37-
knowledge_base_display_names=prompt_request.knowledge_base_display_names
37+
knowledge_base_display_names=prompt_request.knowledge_base_display_names,
38+
has_selected_resources=prompt_request.has_selected_resources,
3839
), media_type="text/event-stream")
3940
except Exception as e:
4041
logger.exception(f"Error occurred while generating system prompt: {e}")

0 commit comments

Comments
 (0)