Skip to content

Commit 8957277

Browse files
authored
Feature/agent runtime optimze 0312 (#162)
1 parent 3954bae commit 8957277

264 files changed

Lines changed: 15034 additions & 1369 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.

assets/schema/derisk.sql

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use derisk;
99
-- MySQL DDL Script for Derisk
1010
-- Version: 0.3.0
1111
-- Generated from SQLAlchemy ORM Models
12-
-- Generated: 2026-03-12 14:20:36
12+
-- Generated: 2026-03-15 19:47:08
1313
-- ============================================================
1414

1515
SET NAMES utf8mb4;
@@ -37,6 +37,27 @@ CREATE TABLE IF NOT EXISTS `derisk_cluster_registry_instance` (
3737
UNIQUE KEY `uk_model_instance` (`model_name`, `host`, `port`, `sys_code`)
3838
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3939

40+
-- Table: streaming_tool_config
41+
-- Source Model: StreamingToolConfig
42+
CREATE TABLE IF NOT EXISTS `streaming_tool_config` (
43+
`id` BIGINT NOT NULL AUTO_INCREMENT,
44+
`app_code` VARCHAR(128) NOT NULL COMMENT '应用代码',
45+
`tool_name` VARCHAR(128) NOT NULL COMMENT '工具名称',
46+
`tool_display_name` VARCHAR(256) NULL COMMENT '工具显示名称',
47+
`tool_description` TEXT NULL COMMENT '工具描述',
48+
`param_configs` JSON NOT NULL COMMENT '参数配置',
49+
`global_threshold` INT NULL DEFAULT 256 COMMENT '全局阈值',
50+
`global_strategy` VARCHAR(32) NULL COMMENT '全局策略',
51+
`global_renderer` VARCHAR(32) NULL COMMENT '全局渲染器',
52+
`enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用流式',
53+
`priority` INT NOT NULL DEFAULT 0 COMMENT '优先级',
54+
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
55+
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
56+
`created_by` VARCHAR(128) NULL COMMENT '创建人',
57+
`updated_by` VARCHAR(128) NULL COMMENT '更新人',
58+
PRIMARY KEY (`id`)
59+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
60+
4061
-- Table: chat_history
4162
-- Source Model: ChatHistoryEntity
4263
CREATE TABLE IF NOT EXISTS `chat_history` (

packages/derisk-app/src/derisk_app/app.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ def mount_routers(app: FastAPI, param: Optional[ApplicationConfig] = None):
115115
app.include_router(create_dashboard_routes(), prefix="/api/v1", tags=["Monitoring"])
116116
logger.info("[Monitoring] Dashboard API routes registered at /api/v1/monitoring")
117117

118+
# Streaming Configuration API routes
119+
from derisk_serve.streaming.api import router as streaming_config_router
120+
121+
app.include_router(streaming_config_router, tags=["Streaming Config"])
122+
logger.info("[Streaming] Config API routes registered at /api/v1/streaming-config")
123+
118124

119125
def mount_static_files(app: FastAPI, param: ApplicationConfig):
120126
if param.service.web.new_web_ui:

packages/derisk-app/src/derisk_app/initialization/db_model_initialization.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Import all models to make sure they are registered with SQLAlchemy."""
22

33
from derisk.model.cluster.registry_impl.db_storage import ModelInstanceEntity
4+
from derisk.model.streaming.db_models import StreamingToolConfig
45
from derisk.storage.chat_history.chat_history_db import (
56
ChatHistoryEntity,
67
ChatHistoryMessageEntity,
@@ -23,7 +24,9 @@
2324
from derisk_serve.config.models.models import ServeEntity as ConfigServeEntity
2425
from derisk_serve.building.app.models.models import ServeEntity as AppServeEntity
2526
from derisk_serve.building.app.models.models_details import AppDetailServeEntity
26-
from derisk_serve.building.config.models.models import ServeEntity as AppConfigServeEntity
27+
from derisk_serve.building.config.models.models import (
28+
ServeEntity as AppConfigServeEntity,
29+
)
2730
from derisk_serve.mcp.models.models import ServeEntity as MCPServeEntity
2831

2932
_MODELS = [
@@ -47,4 +50,5 @@
4750
AppConfigServeEntity,
4851
MCPServeEntity,
4952
ChannelEntity,
53+
StreamingToolConfig,
5054
]

packages/derisk-app/src/derisk_app/initialization/serve_initialization.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,18 @@ def register_serve_apps(
457457
)
458458

459459
# ################################ Scene Serve Register End ################
460+
461+
# ################################ Streaming Config Serve Register Begin ################
462+
from derisk_serve.streaming.serve import Serve as StreamingConfigServe
463+
464+
system_app.register(
465+
StreamingConfigServe,
466+
config=get_config(
467+
serve_configs,
468+
StreamingConfigServe.name,
469+
derisk_serve.streaming.serve.ServeConfig,
470+
api_keys=global_api_keys,
471+
),
472+
)
473+
474+
# ################################ Streaming Config Serve Register End ################

packages/derisk-app/src/derisk_app/openapi/api_v1/api_v1.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,6 @@ async def chat_completions(
352352
# Adapt OpenAI messages format to user_input
353353
if not dialogue.user_input and dialogue.messages:
354354
try:
355-
# Extract the last user message content
356355
last_message = next(
357356
(
358357
msg
@@ -395,6 +394,75 @@ async def chat_completions(
395394
dialogue.user_input, ignore_unknown_media=True
396395
)
397396

397+
# 处理文件输入:提取文件引用并增强消息
398+
sandbox_file_refs = []
399+
if in_message.has_media:
400+
try:
401+
from derisk_serve.agent.file_io import (
402+
process_chat_input_files,
403+
build_enhanced_query_with_files,
404+
SandboxFileRef,
405+
)
406+
407+
user_inputs = []
408+
if isinstance(in_message.content, list):
409+
for media in in_message.content:
410+
if hasattr(media, "type") and hasattr(media, "object"):
411+
if media.type == "image" and media.object.format.startswith(
412+
"url"
413+
):
414+
user_inputs.append(
415+
{
416+
"type": "image_url",
417+
"image_url": {"url": str(media.object.data)},
418+
}
419+
)
420+
elif (
421+
media.type == "file"
422+
and media.object.format.startswith("url")
423+
):
424+
user_inputs.append(
425+
{
426+
"type": "file_url",
427+
"file_url": {"url": str(media.object.data)},
428+
}
429+
)
430+
431+
if user_inputs:
432+
result = await process_chat_input_files(
433+
user_inputs=user_inputs,
434+
sandbox=None,
435+
conv_id=dialogue.conv_uid,
436+
)
437+
sandbox_file_refs = result.sandbox_file_refs
438+
logger.info(
439+
f"[v1/chat] Processed {len(sandbox_file_refs)} files from user input"
440+
)
441+
442+
if sandbox_file_refs and not result.multimodal_contents:
443+
text_content = (
444+
in_message.last_text
445+
if hasattr(in_message, "last_text")
446+
else str(in_message.content)
447+
)
448+
enhanced_text = build_enhanced_query_with_files(
449+
text_content, sandbox_file_refs
450+
)
451+
in_message = HumanMessage(content=enhanced_text)
452+
logger.info("[v1/chat] Enhanced message with file references")
453+
454+
except ImportError:
455+
logger.warning("[v1/chat] file_io module not available")
456+
except Exception as e:
457+
logger.warning(f"[v1/chat] Failed to process files: {e}")
458+
459+
# 将 sandbox_file_refs 传递到 ext_info
460+
if sandbox_file_refs:
461+
dialogue.ext_info["sandbox_file_refs"] = [
462+
ref.to_dict() if hasattr(ref, "to_dict") else ref
463+
for ref in sandbox_file_refs
464+
]
465+
398466
work_mode = dialogue.work_mode or WorkMode.ASYNC
399467

400468
if work_mode == WorkMode.QUICK:
@@ -449,7 +517,6 @@ async def chat_wrapper():
449517
chat_in_params=dialogue.chat_in_params,
450518
**dialogue.ext_info,
451519
)
452-
# result 是 (None, agent_conv_id) 元组,提取会话ID
453520
agent_conv_id = result[1] if result else None
454521
return Result.succ(data={"conv_id": agent_conv_id})
455522
else:
@@ -485,7 +552,6 @@ async def error_text(err_msg):
485552
media_type="text/plain",
486553
)
487554
finally:
488-
# write to recent usage app.
489555
if dialogue.user_name is not None and dialogue.app_code is not None:
490556
user_recent_app_dao.upsert(
491557
user_code=dialogue.user_name,

packages/derisk-app/src/derisk_app/openapi/api_v1/tool_management_api.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,12 @@ def get_sandbox_enabled_from_app(app_id: Optional[str]) -> bool:
5757
.first()
5858
)
5959

60-
if config and config.param_need:
61-
param_need = config.param_need
62-
if isinstance(param_need, str):
63-
param_need = json.loads(param_need)
64-
65-
for item in param_need:
66-
if isinstance(item, dict) and item.get("key") == "sandbox":
67-
sandbox_value = item.get("value", {})
68-
if isinstance(sandbox_value, str):
69-
sandbox_value = json.loads(sandbox_value)
70-
sandbox_type = (
71-
sandbox_value.get("type", "local")
72-
if isinstance(sandbox_value, dict)
73-
else "local"
74-
)
75-
return sandbox_type and sandbox_type != "local"
60+
if config and config.team_context:
61+
team_context = config.team_context
62+
if isinstance(team_context, str):
63+
team_context = json.loads(team_context)
64+
if isinstance(team_context, dict):
65+
return team_context.get("use_sandbox", False)
7666
except Exception as e:
7767
import logging
7868

@@ -129,20 +119,21 @@ async def get_tool_groups(
129119
app_id: Optional[str] = Query(None, description="应用ID"),
130120
agent_name: Optional[str] = Query(None, description="Agent名称"),
131121
lang: str = Query("zh", description="语言(zh/en)"),
122+
sandbox_enabled: Optional[bool] = Query(None, description="是否启用沙箱环境"),
132123
):
133124
"""
134125
获取工具分组列表
135126
136127
返回按分组类型组织的工具列表,包括绑定状态信息。
137-
沙箱状态自动从应用配置中获取
138-
- 如果应用配置了沙箱且 type != "local": 沙箱工具显示为默认绑定
128+
沙箱状态优先使用前端传递的参数,否则从应用配置中获取
129+
- 如果启用沙箱: 沙箱工具显示为默认绑定
139130
- 否则: 本地工具 (read, bash) 显示为默认绑定
140131
"""
141132
try:
142133
ensure_tools_initialized()
143134

144-
# 自动从应用配置中获取沙箱状态
145-
sandbox_enabled = get_sandbox_enabled_from_app(app_id)
135+
if sandbox_enabled is None:
136+
sandbox_enabled = get_sandbox_enabled_from_app(app_id)
146137

147138
groups = tool_manager.get_tool_groups(
148139
app_id=app_id,

packages/derisk-app/src/derisk_app/static/web/404.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/derisk-app/src/derisk_app/static/web/404/index.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/derisk-app/src/derisk_app/static/web/_next/static/8ZaJEcD2RCFq8rMtZkYj-/_buildManifest.js renamed to packages/derisk-app/src/derisk_app/static/web/_next/static/BT3Ifdm8GgJU5o272cKU1/_buildManifest.js

File renamed without changes.

packages/derisk-app/src/derisk_app/static/web/_next/static/8ZaJEcD2RCFq8rMtZkYj-/_ssgManifest.js renamed to packages/derisk-app/src/derisk_app/static/web/_next/static/BT3Ifdm8GgJU5o272cKU1/_ssgManifest.js

File renamed without changes.

0 commit comments

Comments
 (0)