feat: 增加 MCP 研究模板保存工具
This commit is contained in:
@@ -11,7 +11,7 @@ from sqlalchemy import select
|
||||
from ..models import Account, Admin, MCPToken, now
|
||||
from ..security import token_hash
|
||||
|
||||
SCOPES = frozenset({"research:read", "research:refresh", "backtests:execute", "backtests:control"})
|
||||
SCOPES = frozenset({"research:read", "research:refresh", "research:write", "backtests:execute", "backtests:control"})
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -21,6 +21,7 @@ from ..research_access.service import ResearchAccess, ResearchError
|
||||
|
||||
# Name, schema, business method, required scope, description. No generic arbitrary HTTP tool.
|
||||
TOOLS = {
|
||||
"create_research_template": (c.CreateTemplate, "create_template", "research:write", "将调用方大模型研究后自行总结的参数化模板保存到模板工坊,供用户后续批量回测。先用 get_backtest_results 阅读实际指标和检查,选择 1–20 个已完成采集的 source_item_ids,并说明 hypothesis;不要把 completed 当作检查通过。template 使用 {name} 占位符及逐一对应的 variables,字段变量须声明 MATRIX/VECTOR/GROUP,VECTOR 聚合须明确写入表达式。提供唯一名称和 idempotency_key,可附 reference。返回模板 ID、版本和理论组合数;仅核验结构及来源,不验证所有参数组合,不再次调用模型、不执行回测、不覆盖已有模板。"),
|
||||
"get_submission_check": (c.SelfCorrelationReference, "submission_check_context", "research:read", "读取已导入 Alpha 的表达式、Description、snapshot 和缓存检查结果;不发起检查。先核对或生成三段 Description,再调用 check_submission。"),
|
||||
"check_submission": (c.SubmissionCheck, "check_submission", "research:refresh", "对单个待提交 Alpha 写回已获用户授权的 Description 并调用平台 GET /check,返回 job_id。须先用 get_submission_check 获取 snapshot;保留本地自相关门槛和冲突保护。通过 get_refresh_job 查进度、get_submission_check 读结果。无论检查结果如何,都不会调用 /submit 或正式提交 Alpha。"),
|
||||
"get_worldquant_connection": (c.ConnectionReference, "connection", "research:read", "读取 WorldQuant 连接状态及可选认证 job_id 的进度,不发起认证;人工验证在网页完成。"),
|
||||
@@ -69,7 +70,7 @@ class MCPResearchServer:
|
||||
return types.ListToolsResult(tools=[types.Tool(name=name, description=description,
|
||||
inputSchema=schema.model_json_schema(), annotations=types.ToolAnnotations(
|
||||
readOnlyHint=scope == "research:read", destructiveHint=method == "control",
|
||||
idempotentHint=method in {"submit", "control"} or scope == "research:read",
|
||||
idempotentHint=method in {"submit", "control", "create_template"} or scope == "research:read",
|
||||
openWorldHint=method in {"refresh", "submit", "metadata", "check_self_correlation", "check_submission", "authenticate"}))
|
||||
for name, (schema, method, scope, description) in TOOLS.items()
|
||||
if scope in principal.scopes and "research:read" in principal.scopes])
|
||||
@@ -123,7 +124,7 @@ class MCPResearchServer:
|
||||
data = {"error": ResearchError(code, "研究操作失败;可使用原幂等键重试或查询历史", retryable=True).data}
|
||||
db.add(MCPAudit(id=str(uuid4()), token_id=principal.token_id, tool=name,
|
||||
request_id=fingerprint({"request_id": request_id}), input_digest=digest,
|
||||
business_id=data.get("backtest_run_id", data.get("job_id")),
|
||||
business_id=data.get("backtest_run_id", data.get("job_id", data.get("template_id"))),
|
||||
result_code=code, elapsed_ms=int((time.monotonic()-started)*1000)))
|
||||
if not error:
|
||||
if access.wake == "backtests":
|
||||
|
||||
@@ -8,7 +8,7 @@ from sqlalchemy import func, select
|
||||
|
||||
from ..models import Account, MCPToken, now
|
||||
from ..security import require_auth
|
||||
from .auth import create_token
|
||||
from .auth import SCOPES, create_token
|
||||
|
||||
router = APIRouter(prefix="/api/v1/mcp-tokens", tags=["mcp-tokens"], dependencies=[Depends(require_auth)])
|
||||
|
||||
@@ -17,7 +17,7 @@ class TokenInput(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
name: str = Field(min_length=1, max_length=100)
|
||||
days: int = Field(default=90, ge=1, le=365, strict=True)
|
||||
scopes: list[str] = Field(default_factory=lambda: ["research:read"], max_length=4)
|
||||
scopes: list[str] = Field(default_factory=lambda: ["research:read"], max_length=len(SCOPES))
|
||||
|
||||
|
||||
def token_output(row, account):
|
||||
|
||||
Reference in New Issue
Block a user