File size: 4,309 Bytes
f39c319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a273844
f39c319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a273844
 
 
f39c319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a273844
 
 
 
f39c319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { Request, Response, NextFunction } from 'express';
import { MissingMaterialItem } from './models';

export class AppError extends Error {
  public statusCode: number;
  public errorCode: number;

  constructor(message: string, statusCode: number, errorCode: number) {
    super(message);
    this.statusCode = statusCode;
    this.errorCode = errorCode;
    Object.setPrototypeOf(this, AppError.prototype);
  }
}

export class MissingMaterialError extends AppError {
  public missingItems: MissingMaterialItem[];

  constructor(missingItems: MissingMaterialItem[]) {
    super('need_more_material', 400, 4001);
    this.missingItems = missingItems;
    Object.setPrototypeOf(this, MissingMaterialError.prototype);
  }
}

// 请求日志中间件
export const requestLogger = (req: Request, res: Response, next: NextFunction) => {
  const started_at = new Date();
  
  // 拦截 res.send 记录结束时间
  const originalSend = res.send;
  res.send = function (body: any) {
    const ended_at = new Date();
    
    // 提取 case_context
    const case_context = req.body?.case_context || req.body;
    const case_id = case_context?.case_id || 'unknown';
    
    // 提取 meta 数据和 retry_count
    const metadata = case_context?.metadata || {};
    const retry_count = metadata.retry_count || req.headers['x-retry-count'] || req.body?.retry_count || 0;

    // 解析 agent_name
    let agent_name = 'unknown';
    if (req.path.includes('peer-reg-summary')) agent_name = 'AGENT-01';
    else if (req.path.includes('policy-rewrite')) agent_name = 'AGENT-02';
    else if (req.path.includes('compliance-check')) agent_name = 'AGENT-03';
    else if (req.path.includes('legal-pack')) agent_name = 'AGENT-04';
    else if (req.path.includes('log-analysis')) agent_name = 'LOG_ANALYSIS';
    else if (req.path.includes('code-security')) agent_name = 'CODE_SECURITY';
    else if (req.path.includes('regulatory-penalty-summary')) agent_name = 'REG_PENALTY_SUMMARY';

    const status = res.statusCode >= 400 ? 'failed' : 'success';
    const error_code = res.locals.errorCode || 0;
    
    const api_latency_ms = ended_at.getTime() - started_at.getTime();
    const llm_latency_ms = res.locals.llm_latency_ms || 0;

    // 确保绝对不记录 materials,只输出 meta 数据
    console.log(JSON.stringify({
      case_id,
      retry_count,
      agent_name,
      metadata,
      started_at: started_at.toISOString(),
      ended_at: ended_at.toISOString(),
      api_latency_ms,
      llm_latency_ms,
      status,
      error_code
    }));

    return originalSend.call(this, body);
  };

  next();
};

// 全局错误处理中间件
export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
  let statusCode = 500;
  let errorCode = 5000;
  let message = 'Internal Server Error';
  let extraData: any = null;

  if (err instanceof MissingMaterialError) {
    statusCode = err.statusCode;
    errorCode = err.errorCode;
    message = err.message;
    extraData = { missing_materials: err.missingItems };
  } else if (err instanceof AppError) {
    statusCode = err.statusCode;
    errorCode = err.errorCode;
    message = err.message;
  } else if (err.message === 'LLM_INFERENCE_FAILED') {
    statusCode = 500;
    errorCode = 5001;
    message = '智能体推理失败';
  } else if (err.message === 'MISSING_MATERIALS') {
    statusCode = 400;
    errorCode = 4001;
    message = 'need_more_material';
  } else if (err.message === 'INVALID_FORMAT') {
    statusCode = 400;
    errorCode = 4002;
    message = '输入格式错误';
  } else if (err.message === 'MISSING_CONTEXT') {
    statusCode = 500;
    errorCode = 5002;
    message = '外部上下文缺失';
  } else if (err.message === 'MISSING_LLM_API_KEY') {
    statusCode = 500;
    errorCode = 5003;
    message = 'LLM密钥未配置';
  }

  // 记录 errorCode 给 logger 使用
  res.locals.errorCode = errorCode;

  if (err instanceof MissingMaterialError) {
    res.status(statusCode).json({
      schema_version: 'v1.0',
      error_code: 'need_more_material',
      message: message,
      ...(extraData || {})
    });
  } else {
    res.status(statusCode).json({
      error: {
        code: errorCode,
        message: message,
        ...(extraData || {})
      }
    });
  }
};