dream.js 3.27 KB
/**
 * dream —— 经验汇流的 REST 入口([29-DREAM](../../docs/todo/system-design/29-dream-design.md))。
 *
 * 只做一件事:把 `dream-service.js` 已有的能力(`dreamOnce` / `dreamSweep` /
 * `auditDistilled` / `definitionWritability`)从「只能靠脚本或定时器触发」
 * 变成「REST 可查可触发」。不新增业务逻辑。
 */
const express = require('express');
const router = express.Router();
const dream = require('../services/dream-service');

// dreamSweep 串行跑几十个本体、每个可能起两次 LLM 调用,不能塞进一次 HTTP 请求里等。
// 用最小的内存态跟踪「在跑没在跑」+「上一轮结果」,够控制台轮询用。
let sweepState = { sweeping: false, startedAt: null, finishedAt: null, results: null, error: null };

// GET /api/dream/agents — 总览:每个可 dream 的本体,跑没跑过、上次沉淀了几条
router.get('/agents', (req, res) => {
  try {
    const list = dream.listDreamableAgents().map((agentId) => {
      const state = dream.readState(agentId);
      return {
        agentId,
        companies: dream.companiesOf(agentId).length,
        lastRunAt: state.lastRunAt || null,
        lastAccepted: state.lastAccepted || 0,
        lastRejected: state.lastRejected || 0,
      };
    });
    res.json({ agents: list });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// GET /api/dream/agents/:agentId — 详情:状态 + 定义文件能不能写 + 已沉淀经验的可回查列表
router.get('/agents/:agentId', (req, res) => {
  try {
    const { agentId } = req.params;
    res.json({
      agentId,
      companies: dream.companiesOf(agentId),
      state: dream.readState(agentId),
      definitions: dream.definitionWritability(agentId),
      experience: dream.auditDistilled(agentId), // 每条结论并排列出引用的原料原文,回查成本降到几秒
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// POST /api/dream/agents/:agentId/run — 手动跑一次(真会起 LLM 调用,dryRun 只想看会不会有新原料时用)
router.post('/agents/:agentId/run', async (req, res) => {
  try {
    const dryRun = req.body && req.body.dryRun === true;
    const result = await dream.dreamOnce(req.params.agentId, { dryRun });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// POST /api/dream/sweep — 手动触发全量扫描(后台跑,别等;串行跑完可能是几分钟到几十分钟)
router.post('/sweep', (req, res) => {
  if (sweepState.sweeping) return res.status(409).json({ error: '上一轮扫描还没跑完', ...sweepState });
  sweepState = { sweeping: true, startedAt: new Date().toISOString(), finishedAt: null, results: null, error: null };
  dream.dreamSweep()
    .then((results) => { sweepState = { ...sweepState, sweeping: false, finishedAt: new Date().toISOString(), results }; })
    .catch((err) => { sweepState = { ...sweepState, sweeping: false, finishedAt: new Date().toISOString(), error: err.message }; });
  res.status(202).json({ started: true, startedAt: sweepState.startedAt });
});

// GET /api/dream/sweep/status — 轮询上面那个后台任务
router.get('/sweep/status', (req, res) => res.json(sweepState));

module.exports = router;